{
  "schema": "erabazu.public-catalog/v1",
  "language": "ja",
  "notice": "ERABAZU工房のCandidate技術検査を通過した部品群。公開・認証・出荷可否とは別の状態です。",
  "items": [
    {
      "id": "A-01",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "トークン検証 — トークン検証継手（Tsugite）",
      "summary": "トークン検証継手（Tsugite）は、裏方の認証基盤（OIDC/OAuth・JWT）から受け取ったトークンの主張（claims）を検め、alg・aud・iss・exp と、署名が検証済みかどうかの印（signature_valid）のどれか一つでも合わなければ通さない継手（つぎて）です（fail-closed）。署名そのものの検証はしません。signature_valid は呼び出し側が検証して渡します。",
      "problem": "「ログインできた」で終わってしまい、トークンの検証の中身が見えないことを防ぎます。alg: none や許可していない alg、別の相手向けの aud、pin していない発行者、期限切れのトークンを、判定の段階で必ず止めるための検めです。",
      "use_when": [
        "OIDC/OAuth のトークンの alg・aud・iss・exp を、決めた規則どおりに検めたいとき。",
        "alg: none や許可していない alg（例: HS256）のトークンを確実に拒否したいとき。",
        "署名の検証は自分の側（pin した JWKS・鍵）で済ませ、その結果と claims の突き合わせを、通信のない純粋な関数で判定したいとき。",
        "トークンの正当性の判定結果を、認可の判定（policy-facts）や監査・来歴（token-facts）の核へ継ぎたいとき。"
      ],
      "do_not_use_when": [
        "トークンの署名そのものを検証してほしいとき。この継手は署名を検証しません。signature_valid（真偽値）を呼び出し側から受け取り、それが検証済みでなければ拒否するだけです。",
        "IdP や JWKS の取得までまかせたいとき。取得は含みません。",
        "nbf・iat・kid を検めたいとき、または aud が配列のトークンを扱いたいとき。aud は 1 つの値として照合し、配列の aud は扱いません。",
        "認可（誰に何を許すか）を決めたいとき。認可の判定は policy-facts 核へ渡します。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "alg を検めます。alg: none と、許可一覧（allowed_algs）に無い alg（例: HS256）は拒否します。",
        "aud を検めます。許可した aud（allowed_aud）と一致しない aud（aud の混同）は拒否します。",
        "iss を検めます。pin した iss（allowed_iss）以外の発行者は拒否します。",
        "exp を検めます。猶予（leeway_seconds、既定 0）を含めて期限を過ぎたトークンと、exp が無いトークンは拒否します。",
        "signature_valid を検めます。署名が検証済みでない（signature_valid が真でない）トークンは拒否します。",
        "どれか一つでも外れたら { ok: false, errors: [...] } を返し、通しません（fail-closed）。"
      ],
      "what_it_does_not_do": [
        "トークンの署名の実際の検証（pin した JWKS・鍵による検証）はしません。signature_valid は呼び出し側が検証して渡す値で、この継手はその値を信じて判定します。",
        "IdP・JWKS の取得はしません。通信をしない判定だけの部品で、port もありません。",
        "nbf・iat・kid は検めません。配列の aud も扱いません。",
        "検めるのは claims の alg・aud・iss・exp・signature_valid の 5 つだけです。カタログの一言にある「失効」は、この継手では exp による期限切れの拒否（leeway 込み）として扱います。",
        "実 JWKS・鍵には、この部品の中ではつないでいません。配備するときに pin します。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "IdP の使い方の雛形ではありません。"
      ],
      "integration": "src/guards.js の verifyClaims(cfg, claims, now) を呼びます（純粋な関数で、通信はしません）。cfg には allowed_algs・allowed_aud・allowed_iss・leeway_seconds を、claims には { alg, aud, iss, exp, signature_valid } を、now には検証時刻（秒）を渡すと、{ ok: true, errors: [] } か { ok: false, errors: [...] } を返します。利用側が用意するもの: トークンの署名の検証（pin した JWKS・鍵）とその結果の signature_valid、IdP・JWKS の取得。port は無く、外部への副作用はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-02",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "認可判定の参照 — 認証・認可の拡張継手（Tsugite）",
      "summary": "認証・認可の拡張継手（Tsugite）は、A-02 では、外の判断点が出した許可の判断を参照として、pin した許可の方針で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。許可の評価（RBAC/ABAC）はしません。判断は外のもので、PERMIT を受け取っても許可の権限は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "外の判断点の許可の判断を受け渡すとき、pin していない方針の判断を運んでしまうこと、判断を名乗る receipt から許可の権限を導いてしまうこと、そして判断を受け取ったことを承認と取り違えることを防ぎます。",
      "use_when": [
        "外の判断点が出した許可の判断を、参照（decision_ref）として、pin した許可の方針（policy_refs）の範囲でだけ基盤へ運びたいとき。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には decision・decision_ref・permission_authority が添えられます。",
        "判断の受け渡しで外への効果を起こしたくないとき。許可の判断の参照の副作用は NONE です。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "RBAC/ABAC で「この人はこれをしてよいか」を評価させたいとき。この継手は外の判断点の判断を参照として運ぶ継手で、許可の評価の機能は含みません。",
        "受け取った PERMIT を許可の権限として使いたいとき。PERMIT を受け取っても許可の権限は導かず、permission_authority を導いたと名乗る receipt は受理しません。",
        "判断が正しいかを確かめたいとき。許可の判断そのものは確かめません（外の判断と基盤の役目）。",
        "すぐに使える判断点がほしいとき。実の基盤（どの判断点か）は含まず、未定です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "A-02 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
      ],
      "what_it_checks": [
        "方針を検めます。pin していない許可の方針の要求は送りません。空の一覧は何も許しません。",
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "許可の権限を導いたと名乗る receipt は受理しません。",
        "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "許可の評価をしません。この継手は外の判断点の判断を参照として運ぶ継手で、RBAC/ABAC の評価の機能は含みません。許可の判断は外の判断点、許可の方針の事実は核（policy-facts）の役目です。",
        "PERMIT は許可の権限ではありません。判断を受け取ったことから、許可・権限を導きません。",
        "判断を受け取ったことは承認ではありません（approval: false）。",
        "実の基盤（どの判断点か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "直接の識別子のすべてを見分けるものではありません。検出はメールアドレスと電話番号の代表的な形だけです。",
        "特定の IdP や判断点の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に AuthzDecisionRefRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: decision・decision_ref・permission_authority。cfg には pin する一覧 6 つ（policy_refs ほか）がどれも要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-03",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "セッション管理 — セッション・失効伝播継手（Tsugite）",
      "summary": "セッション・失効伝播継手（Tsugite）は、session store への開く（OPEN）・再開（RESUME）・終了（TERMINATE）・失効（REVOKE）の操作を、期限・同時数・既知の状態を検めてから送り、store が返す受領（SessionReceipt）を照合してから session の状態を進める継手（つぎて）です。一つでも合わなければ、送らず、session を使える扱いにしません（fail-closed）。",
      "problem": "「ログイン状態」は当然のものと思われがちですが、期限を過ぎた session、同時数の上限を超えた session、失効・終了したはずの session が使われ続けることがあります。この継手は、session の発行・再開・終了・失効を検めて送り、store の受領を照合できない限り、その session を使える扱いにしないための検めです。",
      "use_when": [
        "session の発行・再開・終了・失効を、外部の session store に対して行い、その結果を照合してから状態を進めたいとき。",
        "session の最長の長さ（max_session_seconds）と同時に使える数の上限（max_concurrency）を、開く時点で必ず守らせたいとき。",
        "ある時刻にある session を使ってよいかを、照合済み・開いている・期限内・未確定の操作が無い、のすべてがそろうときだけ「使ってよい」と判定したいとき。",
        "store の障害で結果が分からないとき、その session を解決まで使えない扱いにしたいとき。"
      ],
      "do_not_use_when": [
        "session があることを、本人確認や権限の代わりにしたいとき。session は本人確認でも権限でもありません（authority: NOT_DERIVED、approval: false）。",
        "トークンそのものを検証したいとき。それは A-01（token-verify-tsugite）の役目で、この継手は session の操作と失効の伝播だけを扱います。",
        "すぐに使える session store がほしいとき。実 session store は含まず、どれにつなぐかはまだ決まっていません。",
        "session の期限や同時数の上限そのものを決めてほしいとき。決定は核（session-facts・policy-facts・timestamp-facts）の役目で、この継手は上限と状態を受け取って検めるだけです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "同時数を検めます。同時に使える session の上限（max_concurrency）を超える OPEN は送りません。",
        "期限と時刻を検めます。期限の巻き戻しと時刻の巻き戻しを拒否します。OPEN には期限（expiry）と同時数の上限（concurrency_limit）を必須とし、OPEN 以外では期限・同時数を変えさせません。",
        "失効・終了した session の再開を拒否します。store が失効後に RESUMED と返したら、受理せず停止します。",
        "受領（SessionReceipt）を照合します。操作と食い違う状態（session_state は op から決まる。NONE・UNKNOWN も照合しない）、別の session の受領、request_digest の偽りがあれば受理せず、以後は HALTED にします。",
        "権限や本人確認を持ち込む受領を拒否します。authority を導いたもの、検証者の無い本人確認済み（VERIFIED）は受理しません。",
        "SecretRef の隣に生の秘密が置かれた要求を拒否します。",
        "失効（REVOKE）の受領には伝播の参照を必須とし、pin した伝播先すべてに証拠つきで届くまでは「未完了」（REVOKED_PROPAGATION_INCOMPLETE）とします。詳しくは A-08（失効伝播）で扱います。",
        "store の障害は UNKNOWN とし、その session は解決まで使えない扱いにします。"
      ],
      "what_it_does_not_do": [
        "session は本人確認でも権限でもありません。どの結果にも authority: NOT_DERIVED と approval: false が付きます。",
        "トークンの検証はしません。A-01（token-verify-tsugite）の役目です。",
        "実 session store と伝播先（各サービスの失効受付）を含みません。どれにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "session の上限や規則を決めません。決定は核（session-facts・policy-facts・timestamp-facts）の役目で、この継手は受け取った上限と状態を検めるだけです。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。なお、OPEN の必須項目などで契約より厳しくした点があります。",
        "A-03 と A-08 は契約上 1 つの部品です。ID ごとの行（A-03・A-08）が、この部品を指します。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "session store の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createSessionClient({ cfg, port }) の apply に SessionOpRequest（OPEN・RESUME・TERMINATE・REVOKE）を渡すと、APPLIED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED・REVOKED_EVERYWHERE・REVOKED_PROPAGATION_INCOMPLETE を返します（approval: false 付き）。isUsable(session_ref, now) で使えるかを判定。利用側が用意するもの: store と伝播の port（apply・propagation）。副作用: EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-04",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "多要素・ステップアップ（問いかけと照合を運ぶ） — 認証・認可の拡張継手（Tsugite）",
      "summary": "認証・認可の拡張継手（Tsugite）は、A-04 では、追加認証の問いかけ（CHALLENGE）と照合（VERIFY）の要求を、pin した要素と資格情報の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。照合は、この継手が同じ本人へ送った問いかけに対してだけ行います。追加認証を満たしても許可や権限ではありません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "重要な操作の直前に追加認証を挟むとき、送っていない問いかけや別の本人の問いかけに対して照合してしまうこと、token の形の値をそのまま運んでしまうこと、そして追加認証を満たしたことを許可と取り違えることを防ぎます。",
      "use_when": [
        "重要な操作の直前だけ、追加認証の問いかけを基盤へ送り、その答えを照合したいとき。",
        "追加認証の要素を、許す要素の一覧（allowed_factors）の範囲にとどめたいとき。",
        "照合を、この継手が同じ本人へ送って CHALLENGED と照合した問いかけに限りたいとき。状態は CHALLENGE→CHALLENGED・FAILED・UNKNOWN、VERIFY→SATISFIED・FAILED・UNKNOWN です。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には step_up_state・satisfied・authorization が添えられます。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "追加認証を満たしたことを、操作の許可として使いたいとき。満たしても許可や権限ではなく、許可を導いたと名乗る receipt は受理しません。",
        "追加認証が正しいか、資格情報が有効かを確かめたいとき。これらは確かめません（外の判断と基盤の役目）。",
        "秘密や token の値そのものを運びたいとき。秘密・token の bytes は運ばず、参照だけです。",
        "すぐに使える追加認証の基盤がほしいとき。実の基盤（どの IdP か）は含まず、未定です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "A-04 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
      ],
      "what_it_checks": [
        "要素を検めます。許す一覧にない要素、契約にない要素の要求は送りません。空の一覧は何も許しません。",
        "照合の相手を検めます。この継手が送っていない問いかけ、別の本人への問いかけ、失敗した問いかけに対する照合は送りません（REFUSED）。",
        "資格情報の参照を検めます。pin していない資格情報の要求は送りません。資格情報の参照・assertion_ref が bytes や token の形なら、pin されていても送りません。",
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "追加認証から許可を導いたと名乗る receipt は受理しません。",
        "receipt の request_id・request_digest・参照が要求と一致しないもの、外への効果を増やすもの、効果が UNKNOWN のものは受理しません。受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "追加認証を満たしたことは、許可や権限ではありません。許可の判断は外の判断点と人・組織の役目で、継手は送る前と受け取った後の検めだけをします。",
        "追加認証の正しさ・資格情報の有効さは確かめません（外の判断と基盤の役目）。",
        "満たしたことは承認ではありません（approval: false）。",
        "実の基盤（どの IdP か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。直接の識別子の検出もメールアドレスと電話番号の代表的な形だけです。",
        "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に StepUpAuthRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: step_up_state・satisfied・authorization。cfg に pin する一覧 6 つが要ります。利用側が用意するもの: port（submit、任意で lookup）。副作用: 問いかけ EXTERNAL_REVERSIBLE、照合 NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-05",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "サービス間認証（ワークロードの認証を運ぶ） — 認証・認可の拡張継手（Tsugite）",
      "summary": "認証・認可の拡張継手（Tsugite）は、A-05 では、ワークロード（サービス）の認証の要求を、pin したワークロードと資格情報の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。秘密や token の bytes は運ばず、参照だけを運びます。認証できたことから権限は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "サービス同士の認証を基盤へ運ぶとき、pin していないワークロードや資格情報で要求を出してしまうこと、資格情報の bytes や token の形の値をそのまま運んでしまうこと、そして認証できたことを権限と取り違えることを防ぎます。",
      "use_when": [
        "サービス（ワークロード）同士の認証の要求を、pin したワークロード（workload_refs）と資格情報の参照（credential_refs）の範囲でだけ基盤へ運びたいとき。",
        "資格情報を bytes ではなく参照だけで扱いたいとき。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には auth_state・authority が添えられます。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "Workload Identity や mTLS の仕組みそのものがほしいとき。この継手はワークロードの認証の要求を参照で運ぶ継手で、その仕組みの機能は含みません。",
        "ワークロードが認証できたことを、権限として使いたいとき。権限は導かず、ワークロードの権限を導いたと名乗る receipt は受理しません。",
        "資格情報が有効かを確かめたいとき。資格情報の有効さは確かめません（外の判断と基盤の役目）。",
        "資格情報や token の値そのものを運びたいとき。秘密・token の bytes は運ばず、参照だけです。",
        "すぐに使える認証の基盤がほしいとき。実の基盤は含まず、未定です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "A-05 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
      ],
      "what_it_checks": [
        "ワークロードを検めます。pin していないワークロードの要求は送りません。空の一覧は何も許しません。",
        "資格情報の参照を検めます。pin していない資格情報の要求は送りません。資格情報の参照・assertion_ref が bytes や token の形なら、pin されていても送りません。",
        "参照の形を検めます。パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "ワークロードの権限を導いたと名乗る receipt は受理しません。",
        "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "Workload Identity・mTLS の仕組みそのものは含みません。ワークロードの認証の要求を、参照で検めて運ぶだけです。",
        "認証できたことは権限ではありません。身元や資格情報の状態から、許可・権限を導きません。",
        "資格情報の有効さは確かめません（外の判断と基盤の役目）。認証できたことは承認でもありません（approval: false）。",
        "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。",
        "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に WorkloadAuthRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: auth_state・authority。cfg に pin する一覧 6 つ（workload_refs ほか）が要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE か EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-06",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "APIキー発行・回転（参照で運ぶ・鍵を開示させない） — 認証・認可の拡張継手（Tsugite）",
      "summary": "認証・認可の拡張継手（Tsugite）は、A-06 では、API キーの発行（ISSUE）・回転（ROTATE）・失効（REVOKE）の要求を、pin した鍵と秘密の置き場所の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。鍵の bytes は運ばず、鍵の中身を開示する receipt は受理しません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "外部連携の API キーを払い出し・回し・失効させるとき、pin していない鍵や置き場所を扱ってしまうこと、鍵の bytes をそのまま運んでしまうこと、基盤が鍵の中身を返してくるのを受け取ってしまうこと、そして鍵の状態を権限と取り違えることを防ぎます。",
      "use_when": [
        "API キーの発行・回転・失効の要求を、pin した鍵（key_refs）と鍵の置き場所（secret_refs）の範囲でだけ基盤へ運びたいとき。",
        "新しい鍵を、中身ではなく置き場所の参照（secret_ref）だけで受け渡したいとき。secret_ref は ISSUE・ROTATE のときだけ必須で、状態は ISSUE→ISSUED_REF・ROTATE→ROTATED_REF・REVOKE→REVOKED か FAILED です。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には key_state・key_material_disclosed が添えられます。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "鍵の中身を受け取りたいとき。鍵の中身を開示する receipt は受理しません。",
        "鍵の保管そのものがほしいとき。この継手は発行・回転・失効の要求を参照で基盤へ運ぶ継手で、鍵の保管は含みません（どの鍵の保管かは未定）。",
        "鍵が有効であることを、許可や権限として使いたいとき。資格情報が有効でも、許可や権限ではありません。",
        "資格情報が有効かを確かめたいとき。資格情報の有効さは確かめません（外の判断と基盤の役目）。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "A-06 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
      ],
      "what_it_checks": [
        "鍵と置き場所を検めます。pin していない鍵、pin していない秘密の置き場所の要求は送りません。空の一覧は何も許しません。",
        "secret_ref を検めます。鍵の形の secret_ref は、pin されていても送りません。REVOKE に secret_ref を付けた要求も送りません。",
        "鍵の中身を開示する receipt は受理しません。",
        "参照の形を検めます。パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "鍵の中身を運ばず、受け取りません。扱うのは鍵と置き場所の参照だけです。",
        "鍵の状態から許可・権限を導きません。資格情報の有効さも確かめません（外の判断と基盤の役目）。",
        "発行・回転・失効ができたことは承認ではありません（approval: false）。",
        "実の基盤（どの鍵の保管か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。",
        "特定の鍵の保管サービスの使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に ApiKeyLifecycleRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: key_state・key_material_disclosed。cfg に pin する一覧 6 つが要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-07",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "委任・代理（on-behalf-of）の参照 — 認証・認可の拡張継手（Tsugite）",
      "summary": "認証・認可の拡張継手（Tsugite）は、A-07 では、人・組織から受け取った根拠がある委任の参照だけを検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。ERABAZU は委任の権限を作りません。委任の権限は、外の人・組織の根拠からだけ来ます。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "「本人に代わって」の委任を基盤へ運ぶとき、人・組織の根拠の無い委任を運んでしまうこと、ERABAZU 自身を委任の出どころにしてしまうこと、そして委任の権限を作ったと名乗る receipt を受け取ってしまうことを防ぎます。",
      "use_when": [
        "人・組織が出した委任の根拠を recordAuthorityEvidence(evidence_ref, origin) で受け取り（origin は HUMAN か ORGANIZATION だけ）、その根拠がある委任の参照だけを基盤へ運びたいとき。",
        "委任の当事者を、メールアドレス・電話番号ではなく仮名の参照だけで扱いたいとき。",
        "委任の受け渡しで外への効果を起こしたくないとき。委任の参照の副作用は NONE です。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には delegation_state・authority_origination が添えられます。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "委任の権限を ERABAZU に作らせたいとき、または権限の受け渡しそのものをさせたいとき。この継手は委任の参照を運ぶ継手で、ERABAZU は委任の権限を作りません。",
        "人・組織の根拠が無いまま委任を運びたいとき。その要求は送りません（REFUSED）。",
        "委任が本物かを確かめたいとき。委任の根拠の中身は検めず、人・組織から受け取った参照かだけを見ます。",
        "すぐに使える基盤がほしいとき。実の基盤は含まず、未定です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "A-07 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
      ],
      "what_it_checks": [
        "委任の根拠を検めます。根拠の無い委任、recordAuthorityEvidence で受け取っていない根拠による委任、出どころを ERABAZU とした委任は送りません。",
        "委任の権限を作ったと名乗る receipt は受理しません。",
        "当事者の参照と参照の形を検めます。メールアドレス・電話番号の形の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "ERABAZU は委任の権限を作りません。委任の権限は外の人・組織の根拠からだけ来ます。この継手は委任の参照を運ぶ継手で、権限の受け渡しの機能は含みません。",
        "委任の根拠の中身（本物の委任か）は検めません。人・組織から受け取った参照かだけを見ます。",
        "委任を運べたことは承認ではありません（approval: false）。",
        "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "直接の識別子のすべてを見分けるものではありません。検出はメールアドレスと電話番号の代表的な形だけです。",
        "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で人・組織の根拠を受け取ってから、submit(request) に DelegationRefRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。DONE の result: delegation_state・authority_origination。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-08",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "失効伝播 — セッション・失効伝播継手（Tsugite）",
      "summary": "セッション・失効伝播継手（Tsugite）は、session の失効（REVOKE）を store へ送って受領を照合し、pin した伝播先すべてから証拠つきの到達の受領がそろうまで失効を「未完了」とする継手（つぎて）です。未完了のあいだも、その session は使えない扱いにします（fail-closed）。",
      "problem": "「ログアウトしたのに残る」事故を防ぎます。失効を一か所で受け付けただけで「全体に行き渡った」と扱ってしまうこと、そして証拠の無い到達の知らせや pin していない先からの知らせを信じてしまうことを防ぎ、失効を全系へ行き渡らせたかを照合できる形で確かめるための検めです。",
      "use_when": [
        "ログアウトや失効を、決めた伝播先（propagation_targets）すべてに届けたかどうかを、到達の受領で確かめたいとき。",
        "どの伝播先にまだ届いていないかを知りたいとき。未完了のときは、届いていない先（missing_targets）を添えて返します。",
        "受領の不整合で停止している最中でも、失効だけは止めずに送りたいとき。この継手が知らない session の失効も受け付けます。",
        "失効した session が、その後に再開されないようにしたいとき。"
      ],
      "do_not_use_when": [
        "伝播先への失効の送り届けそのものをまかせたいとき。到達は、port（propagation）が返す到達の受領で検めるだけで、伝播先の実装は含みません。",
        "すぐに使える伝播先や session store がほしいとき。実 session store と伝播先（各サービスの失効受付）は未定です。",
        "トークンそのものを失効・検証したいとき。トークンの検証は A-01（token-verify-tsugite）の役目で、この継手は session の操作と失効の伝播だけを扱います。",
        "失効が行き渡ったことを、本人確認や権限・承認として扱いたいとき。どの結果も authority: NOT_DERIVED・approval: false です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "伝播先の欠けを検めます。pin した伝播先のどれか一つでも到達の受領が無ければ、完了とせず REVOKED_PROPAGATION_INCOMPLETE を返します。",
        "到達の証拠を検めます。証拠（evidence_ref）の無い到達の知らせは、届いたとは認めません。",
        "伝播先の pin を検めます。pin していない先からの到達の知らせは、届いたとは認めません。",
        "失効の受領を照合します。REVOKE の受領には伝播の参照を必須とし、状態の食い違い（REVOKE なら REVOKED）、別の session の受領、request_digest の偽りがあれば受理しません。",
        "失効後の残存を拒否します。失効・終了した session は再開せず、store が失効後に RESUMED と返したら受理せず停止します。",
        "失効が完了していても未完了でも、その session は使えない扱いにします。store の障害（UNKNOWN）のときも、解決まで使えない扱いです。"
      ],
      "what_it_does_not_do": [
        "伝播先へ失効を届ける仕組みそのものは持ちません。到達の受領の形（target_ref・session_ref・revoked・evidence_ref）は契約に無く、継手の側の定めです。",
        "実 session store と伝播先（各サービスの失効受付）を含みません。port の実装と pin する対象は未定で、接続するときに別の関門で決めます。",
        "トークンの検証・失効はしません。トークンの検証は A-01（token-verify-tsugite）の役目です。",
        "失効が行き渡ったことは、本人確認でも権限でもありません（authority: NOT_DERIVED、approval: false）。",
        "A-08 は A-03（セッション管理）と契約上 1 つの部品です。ID ごとの行（A-03・A-08）が、この部品を指します。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "session store の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createSessionClient({ cfg, port }) の apply に REVOKE の SessionOpRequest を渡すと、REVOKED_EVERYWHERE か REVOKED_PROPAGATION_INCOMPLETE（missing_targets 付き）などを返します（authority: NOT_DERIVED・approval: false 付き）。伝播先は cfg の propagation_targets に pin します。利用側が用意するもの: store の port（apply）と、到達の受領の一覧を返す port（propagation）。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-09",
      "category": "A/認証・認可",
      "kind": "Tsugite",
      "title": "組織ドメインの管理権の照会 — 組織のドメインの管理権の照会の継手（Tsugite）",
      "summary": "組織のドメインの管理権の照会の継手（Tsugite）は、組織のドメインに置かれた印を、DNS TXT（`_kokoroamu.<domain>`）か HTTPS の決まった場所（`/.well-known/kokoroamu-org.json`）で port に照会させ、返った receipt（OrgDomainControlReceipt）を契約どおりに照合して、受け取ったままの答えを運ぶ継手（つぎて）です。照会できた・印が見つかったことを、管理権の証明として扱いません（fail-closed）。",
      "problem": "「ドメインを確認しました」と言うとき、どこを読み、どの答えを信じたかが見えなくなることを防ぎます。名前や URL はドメインだけから組み立て、転送・TLS・DNSSEC・大きさを検め、受け取ったままの答えを digest とともに運びます。照会を管理権の証明と取り違えず、答えを別の目的に使い回しません。",
      "use_when": [
        "組織のドメインに置かれた印を、DNS TXT（`_kokoroamu.<domain>`、method DNS_TXT）か HTTPS の決まった場所（`/.well-known/kokoroamu-org.json`、method HTTPS_WELL_KNOWN）で照会し、その答えをあとで読む側へ渡したいとき。",
        "呼ぶ側にはドメインだけを渡させ、DNS の名前・URL・port 番号・path は呼ぶ側に決めさせたくないとき。",
        "1 つのドメインを、目的ごとに別々の照会で使いたいとき（今の目的の表は `seat8`）。目的は依頼と答えの両方に入り、答えは別の目的に使い回しません（reusable_for_other_purpose false）。",
        "TXT の行を受け取った順のまま、HTTPS の本文を bytes（base64）のまま、それぞれの digest とともに残したいとき。"
      ],
      "do_not_use_when": [
        "印を読んで、目的と鍵 ID を照合し、期限を決めたいとき。それは A-10 の部品（org-domain-claim-primitive）と規則・人の役目で、この継手は照会だけをします。",
        "照会の結果を、そのまま管理権の証明として使いたいとき。この継手の答えは、管理権を認めるものではありません（ownership_decision NOT_DERIVED）。",
        "任意の URL・port 番号・path を照会したいとき。要求にその欄は無く、受け取りません。",
        "すぐに使える照会の仕組みがほしいとき。実の DNS・HTTPS への照会は 0 回で、port の実装・resolver・信頼する CA の一覧・設定の値は、接続するときに別の関門で決めます。",
        "照会の記録を長く残したいとき。記録はこの継手の中だけで、再起動で消えます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定と目的を検めます。要求の contract_pin が設定 contract_schema_digest と違うとき、設定が無いとき、目的が allowed_purposes に無いとき、resolver が resolver_profile_refs に無いときは送りません（REFUSED）。空の表は何も許しません。",
        "receipt の名前と URL が、ドメインから組み立てたものと同じことを照合します。答えの目的・ドメイン・鍵 ID が依頼と違う receipt は受理しません。",
        "答えの中身を照合します。TXT の順・行数・digest、本文の大きさ・digest・content type（JSON か text、charset=utf-8 だけ）が合わない答えは受理しません。受け取りと要求の文字列は、正しい Unicode のときだけ受け取ります。",
        "転送と TLS を検めます。別のホスト・http・上限を超える転送をたどった答えは受理しません（たどらなかった転送は REDIRECT_REFUSED）。TLS の検めが外れた FOUND・NOT_FOUND、DNSSEC の BOGUS を含む答え、TIMEOUT の途中の答えも受理しません。",
        "つないだアドレス（connected_address）を検めます。IANA の特別用途アドレスの登録の、そのアドレスを含むいちばん狭い行で global のものだけを受け取ります。NAT64 の prefix の中へつないだ答えは、RFC 6052 で取り出した中の IPv4 も global のときだけ受け取ります。",
        "判断しない印と効果を照合します。claim_evaluation NOT_PERFORMED・ownership_decision NOT_DERIVED・side effect NONE を守らない receipt は受理せず、照合できない receipt のあとは停止します（RECEIPT_REJECTED のあとは HALTED）。",
        "同じ request_id の違う中身は送りません。port の障害や時間切れ（limits.timeout_ms と猶予 1 秒）は UNKNOWN とし、同じ要求を送り直して解きます（port.lookup があればまずそれで）。"
      ],
      "what_it_does_not_do": [
        "印（`v=kokoroamu-org1`）を読む・目的と鍵 ID を照合する・期限を決める・管理権を認めることはしません。照会できた・印が見つかったことは、管理権の証明ではありません。",
        "解決したアドレスがすべて global だったか、cookie・資格情報を送らなかったか、名前と URL を実際にドメインから組み立てたかは確かめません（port の役目）。この継手が検めるのは、receipt の connected_address が global であることまでです。",
        "DNSSEC の SECURE・INSECURE、TLS の 3 つの検めの結果、TTL は port の申告で、この継手は形と組み合わせだけを検めます。",
        "global の範囲の表は、IANA の登録をページとして読んだ写しで、生の登録の bytes とは照らしていません。公開の前に生の CSV と照らし直す必要があります。",
        "local の NAT64 の prefix を知りません。nat64 を返さない port の答えでは、local の prefix の中に埋められた IPv4 が global かを照らせません。",
        "何も書きません（side effect NONE）。運べた・照会できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/org-domain-control/v1（OrgDomainControlRequest / OrgDomainControlReceipt）でつなぎます。createOrgDomainControlClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。利用側の用意: 照会の port（submit と任意の lookup）と cfg（contract_schema_digest・allowed_purposes・resolver_profile_refs）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-10",
      "category": "A/認証・認可",
      "kind": "Primitive",
      "title": "組織ドメインの印の読み取り — 組織のドメインの印の読み取りと照合の部品（Primitive）",
      "summary": "組織のドメインの印の読み取りと照合の部品（Primitive）は、会社（組織）が自分のドメインに置いた印（DNS TXT の `_kokoroamu.<domain>` の行、または HTTPS の `/.well-known/kokoroamu-org.json` の JSON 文書）を、照会で受け取った生の答えから読み、目的と鍵 ID の完全一致で照合して、期限つきの記録（claim）を作る単独の部品です。読めない行・ほかの用途の行は捨てずに事実として返します。",
      "problem": "印の書き方の揺れや期限切れは、表から見えません。この部品は、知らない欄・重なる欄・知らない版の印を読まず、目的は呼ぶ側の allowed_purposes にあるものだけを通します。1 つのドメインの確認が別の目的に流用されて権限が広がることを防ぎ、期限は観測の時刻と日数から決めます。",
      "use_when": [
        "DNS TXT の答え（txt_records）か、well-known の本文（body_base64・content_type）から、印（目的と鍵 ID）の一覧を読みたいとき（parseMarks）。",
        "ある目的・ある鍵 ID の印があるかを文字列の完全一致で確かめ、domain・method・purpose・key_id・observed_at・expires_at の記録（claim）を作りたいとき（checkClaim）。",
        "読めない行・ほかの用途の行（SPF など）を、黙って捨てずに理由つき（ignored の reason）で残したいとき。",
        "記録が期限の内かを、渡した時刻（at）で確かめたいとき（isClaimCurrent。外なら reason は NOT_YET_OBSERVED か EXPIRED）。",
        "記録から、attestation の属性（domain:<domain> と purpose:<purpose> をこの順に 1 つずつ）を作りたいとき（claimToAttestationAttributes）。",
        "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。目的の表は部品の中に持たず、呼ぶ側が allowed_purposes で渡します。"
      ],
      "do_not_use_when": [
        "印がそのドメインから来たこと（DNS の名前・DNSSEC・HTTPS の証明書・リダイレクト）を確かめたいとき。この部品は、渡された答えがそのドメインのものだとして読みます。照会とその検めは、照会の部品（A-09 の案）と呼ぶ側の役目です。",
        "ドメインの持ち主の本人性（個人・組織の実在）を確かめたいとき。この部品が読むのは、ドメインを管理していることの印だけです。",
        "大文字・末尾の点・前後の空白を含むドメインや IP アドレスを、直して受け取ってほしいとき。名前は直さずに INVALID_DOMAIN で断ります。",
        "国際化ドメインを Unicode の形で渡したいとき。xn-- の形だけを受け取り、punycode の中身は確かめません。",
        "有効期間を 90 日より長くしたいとき。valid_days は 1〜90 の整数だけで、外れると INVALID_VALID_DAYS で止めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "DNS TXT の行は、最初の欄がちょうど決まった版の名前（`v=kokoroamu-org1`）で、残りが purpose=<目的> と key=<鍵 ID> を 1 つずつのときだけ印として読みます。知らない欄・重なる欄・空の欄・欠けた欄・知らない版の行は読まず、ignored に理由（UNKNOWN_FIELD・DUPLICATE_FIELD・MISSING_FIELD・UNKNOWN_VERSION・MALFORMED_MARK など）を付けて返します。",
        "4096 文字を超える行は TOO_LONG、UTF-8 でない行（{ undecodable_base64 } の形）は復号せずに NOT_UTF8、文字列でない行は NOT_TEXT として ignored に入れます。txt_records が配列でない・64 個を超えるときは INVALID_RECORDS・TOO_MANY_RECORDS で止めます。",
        "well-known の本文は、content_type が application/json・text/plain（任意で ; charset=utf-8）でないとき UNSUPPORTED_CONTENT_TYPE、body_base64 が正準の base64 でない・復号して 64 KiB を超えるとき INVALID_BODY で止めます。UTF-8 でない・JSON でない・文書の形が違う・entries が 1000 個を超える本文は、NOT_UTF8・NOT_JSON・MALFORMED_DOCUMENT・TOO_MANY_ENTRIES の事実で返します。",
        "purpose が allowed_purposes に無いときは matched: false で、reasons に PURPOSE_NOT_ALLOWED を記します。同じ鍵 ID でも、ほかの目的の印では通さず KEY_UNDER_OTHER_PURPOSE を記します。",
        "目的と鍵 ID は文字列の完全一致で比べます。鍵 ID は大文字と小文字を区別し、Unicode の正規化・大文字小文字の畳み込みはしません。",
        "domain・purpose・key_id・allowed_purposes・observed_at・valid_days・marks の形が違うとき、INVALID_DOMAIN・INVALID_PURPOSE・INVALID_KEY_ID・INVALID_ALLOWED_PURPOSES・INVALID_OBSERVED_AT・INVALID_VALID_DAYS・INVALID_MARKS で止めます。ドメインは 242 文字まで、時刻は UTC の Z の形で 2000〜9998 年、marks は 2000 個までです。",
        "isClaimCurrent・claimToAttestationAttributes に渡す claim の形が違うとき（余分なキー・期限が observed_at より後でない・90 日を超える）、INVALID_CLAIM で止めます。at の形が違うときは INVALID_AT です。claimToAttestationAttributes だけは OrgDomainClaimError を投げます。",
        "入力がオブジェクトでないとき INVALID_INPUT、source が DNS_TXT・HTTPS_WELL_KNOWN のどちらでもないとき UNKNOWN_SOURCE で止めます。止めたときは { ok: false, errors: [{ code }] } を返します。"
      ],
      "what_it_does_not_do": [
        "matched: true は、渡された答えの中にこの目的とこの鍵 ID の印があったという事実だけです。ドメインの持ち主の本人性、印がそのドメインから来たこと、attestation に署名してよいことは示しません。署名するかは呼ぶ側が決めます。",
        "DNS・HTTPS の照会はしません。外の境界・状態・乱数を持たず、時計も読みません（時刻はすべて入力）。",
        "目的の表は持ちません。どの目的を許すか（allowed_purposes）は呼ぶ側が渡します。",
        "JSON の重なるキーは JSON.parse のとおり最後の値を使い、重なりを見つけて断ることはしません。",
        "{ undecodable_base64 } の値は復号しません。正準の base64 か・本当に UTF-8 でないかは確かめず（受け取りの側で検めること）、その形の行を印として読みません。",
        "記録（claim）ができたことは、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "DNS や HTTPS の照会の雛形ではありません。"
      ],
      "integration": "src/org-domain-claim.js の parseMarks・checkClaim・isClaimCurrent（投げない）と claimToAttestationAttributes（投げる）を呼びます。parseMarks は txt_records か body_base64・content_type を受け取り marks と ignored を返します。checkClaim は domain・purpose・key_id・allowed_purposes・marks・observed_at・valid_days を受け取り matched・reasons・claim を返します。答えの形は契約 org-domain-control/v1 に合わせています。副作用・状態・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "A-11",
      "category": "A/認証・認可",
      "kind": "Component",
      "title": "組織ドメインの管理権の確かめ — 組織のドメインの管理権の確かめの部品（Component）",
      "summary": "組織のドメインの管理権の確かめの部品（Component）は、照会の継手（A-09）と印の照合の部品(Primitive)（A-10）を束ね、check({ domain, purpose, key_id }) を 1 回で行う部品です。まず DNS TXT（`_kokoroamu.<domain>`）を照会し、目的と鍵 ID がちょうど一致する印があり期限内なら MATCHED（dns_txt）。そうでなければ HTTPS の well-known を照会し、一致すれば MATCHED（https_well_known）。両方が答えて一致が無ければ NOT_FOUND、決められないときは UNAVAILABLE です（fail-closed）。MATCHED は管理権の判断ではありません。",
      "problem": "組織の確認の画面の裏で、照会の順と断る条件を決めておくための部品です。ほかの目的・ほかの鍵の印で通してしまうこと、別のドメイン・目的・鍵・確かめ方の答えを取り違えること、決められないのに「無い」と決めてしまうこと、期限の判断を自分で作ってしまうことを防ぎます。",
      "use_when": [
        "組織のドメインに、決めた目的と鍵 ID（16 桁の小文字 hex）の印があるかを、DNS を先に、次に HTTPS の順で 1 回の呼び出しで確かめたいとき。",
        "照会してよい目的を設定の表（allowed_purposes、1〜64 個。今は `seat8`）に限り、表に無い目的では照会もしないようにしたいとき。",
        "結果を MATCHED・NOT_FOUND・UNAVAILABLE の 3 つで受け取り、「両方が答えて一致が無い」と「決められなかった（後でやり直せる）」を分けて扱いたいとき。",
        "印の期限の判断を、A-10 の isClaimCurrent に、照会の受け取りの後に読んだ時刻で任せたいとき（記録の有効日数は valid_days、1〜90）。",
        "MATCHED のとき、決めた照会の受け取りの request_id（receipt_ref）と照会を送る前の時刻（observed_at）を、後の記録に添えたいとき。"
      ],
      "do_not_use_when": [
        "MATCHED を、ドメインの管理権の判断・署名してよいことの根拠として使いたいとき。MATCHED は「この目的とこの鍵 ID の印が、照会の時点でこのドメインの DNS か well-known にあった」という事実だけです。",
        "同じ端末・ドメインへの照会の回数の上限まで任せたいとき。回数の上限は呼ぶ側の規則と人の役目です。",
        "答えない照会の port を直接つなぐとき。この部品は待ち時間を切らないので、check が終わりません（本物の A-09 のクライアントは limits.timeout_ms に 1 秒を足した時間で UNKNOWN を返します）。",
        "receipt_ref が指す受け取りを、再起動の後も引けることが要るとき。A-09 のクライアントの記録はプロセスの中だけで、再起動で消えます。呼ぶ側の記録に頼ります。",
        "attestation の属性まで作らせたいとき。A-10 の claimToAttestationAttributes は使いません。属性を作るのは呼ぶ側です。",
        "本物の DNS・HTTPS で確かめた部品が要るとき。実の照会は 0 回です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違うとき INVALID_OPTIONS で止めます。check の入力がオブジェクトでない・読めないとき INVALID_INPUT、domain が契約の DomainName（242 文字まで）でないとき INVALID_DOMAIN、purpose が表に無いとき PURPOSE_NOT_ALLOWED、key_id が 16 桁の小文字 hex でないとき INVALID_KEY_ID で断ります。断るときは照会 0 回・時計を読まず、state は null です。",
        "時計が投げた・数でない・2000〜9998 年の外のとき、CLOCK_FAILED でそこで止めて UNAVAILABLE にします（それ以上照会しません）。",
        "A-09 の答えが REFUSED のとき LOOKUP_REFUSED、UNKNOWN のとき LOOKUP_UNKNOWN、RECEIPT_REJECTED か HALTED のとき LOOKUP_HALTED、DONE 以外（ALREADY_DONE を含む）のとき LOOKUP_NOT_DONE、下位の部品が例外を投げた・reject したとき PORT_FAILED で、MATCHED にしません。",
        "DONE でも、結果が聞いたドメイン・目的・鍵 ID・確かめ方を写していない・approval が false でない・result が無いとき、RESULT_MISMATCH で使いません。",
        "照会が答えられなかったとき（TIMEOUT・FAILED・TLS_FAILED・REDIRECT_REFUSED・知らない状態）LOOKUP_FAILED にします。FAILED は理由（PRIVATE_ADDRESS・DNSSEC_BOGUS・CONTENT_TYPE_REFUSED など）によらず「答えられない」です。",
        "A-10 の parseMarks が印を読めないとき MARKS_UNREADABLE、checkClaim が真偽値の matched を返さない・聞いたものと違う claim を返すとき CLAIM_UNCHECKED、isClaimCurrent が current: true を返さないとき（NOT_YET_OBSERVED・EXPIRED など）CLAIM_NOT_CURRENT で、MATCHED にしません。",
        "設定と目的の表は作るときに写し、A-10 へ渡す表も写します。後から options や表を書き換えられても、次の check は広がりません。"
      ],
      "what_it_does_not_do": [
        "組織のドメインの管理権の確かめの部品は継手ではありません。自分の Request/Receipt は無く、外への照会は、束ねた A-09 の継手が自分の契約（org-domain-control/v1）で行います。この部品自身は外へ触れず、照会も読むだけです（side effect NONE）。",
        "照会の検め（受け取りの照合・転送・TLS・DNSSEC・つないだアドレス・content type）は A-09 の側に、印の読み取り・照合・期限は A-10 の側に残ります。印がそのドメインから来たことは A-09 と port の役目で、この部品は A-09 の DONE を信じます。",
        "MATCHED は、管理権の判断でも、権限でも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "受け取りの queried_at（port の申告）と時計の食い違いは照らしません。照会の待ち時間も、この部品は切りません。",
        "本物の DNS・HTTPS には一度も照会していません。試験とファズは代役で、結線の検めは本物の A-09・A-10 のコードと、A-09 の試験用の代役の port を相手に確かめました。",
        "目的の表の置き場所と、今の目的のほかの名前は、この部品では決めていません（契約の残り）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の DNS サービス・HTTP クライアントの使い方の雛形ではありません。"
      ],
      "integration": "src/org-domain-check.js の createOrgDomainCheck(options) で作り、{ ok, errors, instance } を返します。options は allowed_purposes・contract_schema_digest・resolver_profile_ref・limits・valid_days・request_prefix・clock・ports です。check は { ok, errors, state, method, observed_at, receipt_ref, approval: false, … } を返します。port は domainControl.submit（A-09 の継手のクライアント）と A-10 の parseMarks・checkClaim・isClaimCurrent。資格情報は不要です。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-01",
      "category": "B/暗号・信頼",
      "kind": "Tsugite",
      "title": "署名（KMS/HSM） — KMS署名継手（Tsugite）",
      "summary": "KMS署名継手（Tsugite）は、裏方の署名 API（Cloud KMS/HSM）の前に立ち、採択済みの schema・context と、RFC 8785 で正準化した payload だけを受け付け、sequence と previous_hash の連鎖を検めてから署名を頼み、返った署名を pin した公開鍵（PEM）で検証してから返す継手（つぎて）です。一つでも合わなければ KMS を呼ばず拒否します（fail-closed）。",
      "problem": "成果物や記録に電子署名するとき、任意のバイト列に署名してしまうこと、採択していない形式や context に署名してしまうこと、署名の順番（sequence）や前とのつながり（previous_hash）が崩れたまま署名してしまうことを防ぎます。署名 API を、任意の入力を受けない fail-closed の部品にするための検めです。",
      "use_when": [
        "成果物・記録への電子署名を、採択した schema・context と正準化した payload に限って行いたいとき。",
        "署名を context ごとの追記専用（append-only）の鎖（sequence の単調増加と previous_hash の連鎖）に並べたいとき。",
        "返ってきた署名を、公開鍵を都度取りに行かず、pin した PEM でその場で検証してから使いたいとき。",
        "Cloud KMS の AsymmetricSign（EC_SIGN_P256_SHA256）を、JSON 鍵を置かない認証（attached SA / ADC）と鍵単位の最小権限で使う構成にしたいとき。"
      ],
      "do_not_use_when": [
        "任意のバイト列やファイルにそのまま署名したいとき。canonical_payload は RFC 8785 で正準化した文字列に限り、任意の payload は拒否します。",
        "正準化もこの継手にまかせたいとき。RFC 8785 の正準化は呼び出し側が行います。",
        "実際の Cloud KMS・Firestore につないだ状態で試験済みの部品がほしいとき。この部品の試験は、ローカルの P-256 鍵と in-memory の store で行っており、本番の Cloud KMS・Firestore への接続は確かめていません。",
        "署名を、承認や決裁として扱いたいとき。この継手は検めて署名を継ぐだけで、権限は与えません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは融合核（Kernel）が担います。"
      ],
      "what_it_checks": [
        "schema と context を検めます。採択していない schema・context、誤った context（wrong-context）は、KMS を呼ばず 422 で拒否します。",
        "sequence を検めます。巻き戻し・重複・整数でない sequence は、KMS を呼ばず拒否します。",
        "previous_hash を検めます。直前の payload の SHA-256（または GENESIS）とつながらないもの、genesis の規則違反（approval の genesis が定まっていない場合を含む）は拒否します。",
        "payload を検めます。canonical_payload が無いもの、任意の payload は拒否します。",
        "payload の中身を依頼と照らします。payload の context・schema_version が依頼と同じときだけ署名し、違う payload には署名しません。",
        "知らない鍵の指定は 400 で拒否します。鍵は erabazu_publisher か approval_checkpoint です。",
        "署名を検証します。KMS が返した署名を pin した公開鍵（PEM）で検証できなければ、署名を返さず 500 とします。"
      ],
      "what_it_does_not_do": [
        "この部品の試験（単体試験とファズ）では、本番の Cloud KMS・Firestore には接続していません。試験はローカルの P-256 鍵と in-memory の store で行いました。別に、2026-08-15 に本番の ERABAZU HSM 鍵で synthetic の context（erabazu.synthetic-test）に署名し、pin した PEM で検証できた記録があります。",
        "canonical_payload の正準化（RFC 8785）はしません。呼び出し側が行います。",
        "任意の入力には署名しません。context・schema・正準化した payload に限ります。",
        "GET /latest が返す次の sequence・previous_hash は目安（ヒント）です。署名の可否は POST /sign の検めで決まります。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "署名 API の使い方の雛形ではありません。"
      ],
      "integration": "POST /sign に { key, context, schema_version, sequence, previous_hash, canonical_payload } を渡すと、200（signature_der_base64（ECDSA DER）・payload_sha256・verified_with_pinned_pem: true など）、400（知らない鍵）、422（fail-closed、reasons 付き）、500（pin した鍵で検証できない。署名なし）のどれかを返します。入口は src/server.js（handleSign）、設定は app_kms_config.json。利用側が用意するもの: signer の port（本番は Cloud KMS）と store の port（getLatest・commit。本番は Firestore）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-02",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "署名検証（pinned） — 署名検証（pinned）の部品（Primitive）",
      "summary": "署名検証（pinned）の部品（Primitive）は、bytes の上の署名を、使う側が固定した（pinned）公開鍵で手元で検証する単独の部品です。alg は Ed25519 と ES256 だけで、鍵は pinned（key_id → { alg, public_key_jwk }）からだけ取り、要求が鍵を持ち込むことはできません。verify は例外を投げずに { ok, errors } を返し、受け取れない値は決まったコードで止めます（fail-closed）。",
      "problem": "受領物が「本物か」を誰も確かめない、という困りごとに対して、出所・完全性を手元で検証します。そのとき、要求が持ち込んだ鍵で照合してしまうこと、alg を取り違えること、秘密鍵の JWK を pinned に入れた設定の誤り、Node.js の取り込みが受け取ってしまう正準でない符号化を、先に止めます。",
      "use_when": [
        "受け取った bytes の署名を、使う側が決めて固定した（pinned）公開鍵だけで検証したいとき。",
        "alg が Ed25519（RFC 8032 の R||S）か ES256（P-256 の ECDSA・SHA-256、署名は IEEE P1363 の r||s の 64 bytes）の署名を検証したいとき。",
        "例外を投げない検証が要るとき。verify は { ok, errors } を返し、例外を投げず、入力を書き換えません。",
        "JSON の値の署名を検証したいとき。I-08 の部品で正準の bytes にしてから、この部品に渡します。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
      ],
      "do_not_use_when": [
        "署名者が誰か（身元）を確かめたいとき。署名が正しいことは、pinned の鍵の持ち主がこの bytes に署名したことだけを示します。",
        "どの鍵を pin するか・鍵の更新と失効・key_id の割り当てを任せたいとき。これらはこの部品の外（使う側・trust の管理）で決めます。",
        "署名の bytes を一意の識別子に使いたいとき。ES256 の署名は malleable（(r, n - s) も正しい）で、low-S を強制しません。",
        "Ed25519・ES256 のほかの alg（EdDSA・ES384・RS256・none など）や、DER の形の ES256 署名、事前にハッシュした値を検証したいとき。",
        "JWS の全体（ヘッダー・payload の組み立て・base64url の区切り）を扱いたいとき。この部品は署名の bytes の検証だけを受け持ちます。",
        "Ed25519 の厳密さ（小さい位数の点・正準でない点・RFC 8032 と ZIP-215 などの違い）を自分の決まりで揃えたいとき。OpenSSL の実装に従い、小さい位数の点の公開鍵も断りません。",
        "Node.js v24.21.0 のほかの版（OpenSSL の版の違いを含む）で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "要求が素のオブジェクトでないとき NOT_A_REQUEST、alg・key_id・signature・bytes のほかのフィールド（鍵を持ち込む public_key_jwk・seed など）を持つとき UNKNOWN_FIELD で止めます。",
        "alg が \"Ed25519\" でも \"ES256\" でもないとき（大文字小文字の違い・none を含む）UNSUPPORTED_ALGORITHM、key_id が 1〜256 文字の文字列でないとき INVALID_KEY_ID で止めます。",
        "signature が正準の base64url でないとき（パディング・標準の base64 の文字・空白・余りの bit）MALFORMED_SIGNATURE、読むと 64 bytes でないとき（DER を含む）INVALID_SIGNATURE_LENGTH で止めます。",
        "bytes が Uint8Array でないとき（文字列・ほかの型付き配列・DataView・Proxy）、NOT_BYTES で止めます。",
        "pinned が素のオブジェクトでないとき INVALID_PINNED_SET、pinned のどこか（列挙しない項目を含む）に秘密のメンバーを持つ JWK があるとき、どの key_id でも PRIVATE_KEY_REFUSED で止めます。",
        "key_id が pinned の自分のメンバーに無いとき（__proto__ などのプロトタイプの名前を含む）UNKNOWN_KEY、pinned の項目・JWK の形が違うか曲線の上に無い点のとき INVALID_PINNED_KEY で止めます。",
        "要求の alg と pinned の alg が違うとき ALG_MISMATCH、bytes・署名・鍵のどれかが違うとき（Ed25519 の S + L を含む）SIGNATURE_INVALID を返します。",
        "入力を調べること自体が例外になるとき（getter・Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。入力は一度だけ読み、取り込みにはこの関数が作り直した JWK を渡します。"
      ],
      "what_it_does_not_do": [
        "鍵の配布・pin の決定・更新と失効（trust の管理）、JWS・manifest の組み立ては含みません。",
        "署名の作成はしません。秘密鍵を扱うのは KMS などの継手で、この部品は秘密のメンバーを持つ JWK を受け取りません。",
        "楕円曲線の計算は Node.js の crypto（OpenSSL）に任せ、独立には確かめていません。ファズのオラクルも同じ crypto を別の取り込み方で呼ぶだけです。",
        "RFC 8032 の例は 1 つ（例 1）だけで確かめ、ES256 は公開の既知の答えの代わりに、実行時に作った鍵の署名で確かめました。",
        "署名が正しいことは、署名者の身元・法的な受け入れ・権限・承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "ほかの部品・継手の中の署名の検証との互換は確かめていません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の署名のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/signature-verify.js の verify(request, pinned) を呼びます（投げません）。受け取るのは、alg・key_id・signature・bytes（Uint8Array）だけの素のオブジェクト request と、key_id → { alg, public_key_jwk }（公開の JWK だけ）の素のオブジェクト pinned です。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code }] }（code は error_taxonomy）。鍵は pinned で受け取り、取りに行きません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto と util だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-03",
      "category": "B/暗号・信頼",
      "kind": "Component",
      "title": "保管時暗号（envelope） — 保管時暗号（envelope）の部品（Component）",
      "summary": "保管時暗号（envelope）の部品（Component）は、データを封筒暗号にするために、KMS で DEK を作る・包んだ鍵をほどく継手（kms-envelope）と、手元で AES-256-GCM の封筒を作る・開く部品(Primitive)（envelope-encrypt）を束ねる部品です。KMS から受け取るのは handle と包んだ鍵の参照だけで、DEK は port の中の withDataKey から、同じプロセスの中で手元の seal・open にだけ渡ります。封筒の key_ref・包んだ鍵の参照・KMS の鍵が合わなければ KMS に送らず、暗号化の文脈が違えば開きません（fail-closed）。",
      "problem": "「暗号化されているはず」の裏で、平文の DEK が部品の結果や状態に残ること、包んだ鍵の参照や KMS の鍵の合わない封筒を KMS に送ってしまうこと、暗号化の文脈が違う封筒・改ざんされた封筒・違う aad の封筒を開いてしまうことを防ぎます。",
      "use_when": [
        "データ（plaintext、Uint8Array）を、KMS で作らせた DEK で封筒にしたいとき（encrypt）。ports.kms に kms-envelope の継手のクライアント、ports.seal・open に envelope-encrypt の Primitive を注入します。",
        "平文の DEK を、KMS の port の外に出さずに使いたいとき。port の中の keyAccess.withDataKey(handle, fn) が DEK を fn に渡し、この部品は fn の外へ DEK を出しません。",
        "暗号化の文脈を object_ref と context_digest（64 桁の 16 進）に結び付け、違う文脈の封筒を開かないようにしたいとき。",
        "封筒・包んだ鍵（wrapped_key）・暗号化の文脈の digest をまとめて保管し、decrypt で開きたいとき。開く前に、封筒の key_ref と包んだ鍵の参照、KMS の鍵が合うかを確かめます。"
      ],
      "do_not_use_when": [
        "暗号化できた・開けたことを、データを使ってよいこと・権限・承認として使いたいとき。暗号化はアクセス制御ではなく、どの結果にも approval: false・authorization: NOT_DERIVED が付きます。",
        "包んだ鍵・封筒の保管や鍵の回転も任せたいとき。保管は呼び出し側、鍵の回転は B-05 の継手の役目です。",
        "DEK がメモリーから確実に消えることを当てにしたいとき。DEK を port の中に閉じ込めること・ゼロで消すことは port の役目で、確かめていません。",
        "同じ入力から同じ封筒を作りたいとき。IV は Primitive が乱数で作るので、封筒は毎回違います。",
        "16 MiB を超える平文、64 KiB を超える aad を扱うとき。上限は Primitive のままです。",
        "本物の KMS で確かめた部品が要るとき。本物の KMS には一度も接続していません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき（bytes でない・context_digest が 64 桁の 16 進でない・wrapped_key の形など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "封筒の key_ref と包んだ鍵の参照、または包んだ鍵の KMS の鍵と自分の kms_key_ref が合わないとき、KMS に送らずに WRAPPED_KEY_MISMATCH で止めます。",
        "暗号化の文脈が違うとき、KMS が見つければ CONTEXT_MISMATCH、継手が自分の記録から見つければ送らずに KMS_REFUSED で、開きません。",
        "封筒が改ざんされた・aad が違うなど Primitive の open が ok: true を返さないとき OPEN_REFUSED（認証の失敗を含む）、seal が ok: true を返さないとき SEAL_REFUSED で止めます。",
        "KMS の継手の結果が分からないとき KMS_UNKNOWN、送らなかったとき KMS_REFUSED、receipt を照合できず停止した・GENERATED に包んだ鍵の参照や版が無いとき KMS_HALTED、DENIED・KEY_DISABLED・FAILED など望んだ状態でないとき KMS_DENIED で止めます。",
        "handle が無い・継手が使えると true ちょうどで答えないとき HANDLE_NOT_USABLE、withDataKey が投げた・結果を返さない・32 bytes の DEK を渡さないとき KEY_ACCESS_FAILED で止めます。",
        "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。",
        "開いた平文がその封筒を作った平文と同じであること、何かが違うのに開いた回が 0 であることを、別に書いた手本と突き合わせるファズで毎回確かめました（不一致と fail-open は 0）。"
      ],
      "what_it_does_not_do": [
        "保管時暗号（envelope）の部品は継手ではありません。自分の Request/Receipt は無く、KMS とのやり取りは束ねた kms-envelope の継手が自分の契約（kms-envelope/v1）で行います。その要求はどれも intended_side_effect NONE で、この部品自身は外へ触れません。",
        "束ねた部品の意味（包めた・開けた ≠ 使ってよい）は、それぞれの部品の側に残ります。",
        "暗号化できた・開けたことは、承認でも権限でもありません（approval: false・authorization: NOT_DERIVED・plaintext_key_material: NOT_TRANSPORTED、derives_authority: false）。",
        "DEK も handle も持ち続けず、結果に鍵の中身は入りません。状態は通し番号だけで、再起動で消えます。",
        "包んだ鍵・封筒の保管、鍵の回転（B-05）、データを使ってよいかの判断はしません。DEK を port の中に閉じ込めること・期限やプロセスの終わりにゼロで消すことは port の役目で、確かめていません。",
        "本物の KMS には一度も接続していません。単体試験とファズは代役で、本物の 2 部品のコードとの結線は、DEK を中に持つ port の代役を相手に確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の KMS の使い方の雛形ではありません。"
      ],
      "integration": "src/envelope-encryption.js の createEnvelopeEncryption(options) で作ります。受け取るのは kms_key_ref・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。encrypt は envelope・wrapped_key・encryption_context_digest を、decrypt は plaintext を返し、どの結果にも approval: false が付きます。利用側が用意する port は kms（kms-envelope の継手のクライアント）、keyAccess.withDataKey、seal・open（envelope-encrypt）です。副作用は NONE で、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-04",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "伝送時暗号（TLS/mTLS） — 伝送時暗号（TLS/mTLS）の構成検証の部品（Primitive）",
      "summary": "伝送時暗号（TLS/mTLS）の構成検証の部品（Primitive）は、TLS の構成のオブジェクトを、固定した profile（erabazu.primitive/tls-config/modern-2026/v1）の規則 R1〜R10 に照らして検める単独の部品です。TLS そのものはしません（接続しない・証明書を読まない）。規則に合わない値は、決まったコードと位置で止め、違反はすべて決まった順で返します（fail-closed）。",
      "problem": "「鍵マークだけ見て終わり」という困りごとに対して、TLS の構成そのものを版つきの規則で検めます。TLS 1.0/1.1・SSL、弱い cipher、相手の証明書やホスト名を確かめない設定、証明書や鍵の中身（PEM・base64 の塊）の混入を、黙って通しません。",
      "use_when": [
        "TLS の構成（min_version・max_version・ciphers_tls12・reject_unauthorized・verify_hostname・ca_refs・client_auth・alpn）を、使う前に規則 R1〜R10 で検めたいとき。",
        "TLS 1.2 を許す構成で、cipher を ECDHE の AEAD 6 つの名前の一覧（allowlist）だけに限りたいとき。",
        "mTLS のサーバー側の設定として、相手の証明書を求めるとき（client_auth.required が true）に、信頼の起点の参照（client_auth.ca_refs）が揃っているかを検めたいとき。",
        "違反を 1 つずつでなく、すべて決まった順（R1 → … → R10）で、コードと位置（JSON Pointer）付きで受け取りたいとき。",
        "時計・乱数・環境の locale・外の状態に左右されず、外の境界を持たない処理として手元で呼びたいとき。verify は例外を投げず、入力を書き換えません。"
      ],
      "do_not_use_when": [
        "経路の暗号化・相互認証そのもの（TLS の接続）をしてほしいとき。この部品は構成を検めるだけで、接続しません。",
        "証明書そのもの（期限・鍵の長さ・署名のアルゴリズム・鎖）、失効（CRL・OCSP）、相手の名前の一致を確かめたいとき。",
        "証明書の発行・更新や、構成をサーバー・ロードバランサー・クラウドの設定に当てはめることを求めるとき。発行・更新は B-09、当てはめは継手の側に残ります。",
        "OpenSSL の cipher 文字列（: で区切る・! で除く・HIGH などのまとまり）をそのまま検めたいとき。受け取るのは cipher の名前の配列だけです。",
        "TLS 1.3 の cipher suite・鍵交換のグループ（曲線）・署名のアルゴリズム・セッションの再開・0-RTT を決めたいとき。profile では決めていません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "構成が素のオブジェクトでないとき NOT_A_CONFIG、決めていないキー（__proto__ を含む）があるとき UNKNOWN_KEY、必須のキーが無いとき（値が undefined を含む）MISSING_KEY で止めます。",
        "SSLv2・SSLv3・TLSv1・TLSv1.1 のとき VERSION_TOO_OLD、ほかの正しくない版の値のとき INVALID_VERSION、max_version が min_version より下のとき VERSION_RANGE_INVERTED で止めます。",
        "TLS 1.2 を許すのに ciphers_tls12 が無いとき CIPHERS_REQUIRED、空の配列や cipher 文字列のとき INVALID_CIPHER_LIST で止めます。",
        "許した 6 つのほかの cipher（CBC・RC4・3DES・NULL・anon・export・静的 RSA・DHE・TLS 1.3 の名前・大文字小文字や空白の違い・配列の穴）のとき、CIPHER_NOT_ALLOWED で止めます。",
        "reject_unauthorized が true でないとき REJECT_UNAUTHORIZED_REQUIRED、verify_hostname が true でないとき VERIFY_HOSTNAME_REQUIRED で止めます（\"true\"・1 も違反です）。",
        "ca_refs が空のとき INVALID_CA_REFS、参照に PEM の印があるとき CA_REF_LOOKS_LIKE_PEM、128 文字超えや + = 改行を含むとき CA_REF_LOOKS_LIKE_BLOB、決めた文字の並びでないとき INVALID_CA_REF で止めます。",
        "client_auth の形が違うとき INVALID_CLIENT_AUTH、required が true なのに client_auth.ca_refs が無いか空のとき CLIENT_CA_REFS_REQUIRED、alpn が空のとき INVALID_ALPN、id が表示できる ASCII でないとき INVALID_ALPN_ID で止めます。",
        "値を調べること自体が例外になるとき（Proxy の罠・例外を投げる getter）、INSPECTION_FAILED を返し、例外を投げません。getter は値ごとに 1 回だけ読み、その写しで検めます。"
      ],
      "what_it_does_not_do": [
        "経路の暗号化・相互認証を名前に持つ項目ですが、この部品は TLS の構成を検めるだけで、TLS の接続・証明書の読み込みは含みません。構成が規則に合うことは、接続を確かめたことではありません。",
        "証明書そのもの（期限・鍵の長さ・署名のアルゴリズム・鎖）、失効（CRL・OCSP）、相手の名前の一致は確かめません。",
        "実行環境（Node.js・OpenSSL・ロードバランサーなど）がこの構成を本当に守るか、名前の書き方を受け取るかは確かめていません（tls に渡していません）。",
        "ca_refs の参照が何を指すか・解決できるかは確かめません。見た目の検めなので、128 文字以内で決めた文字の並びに合う文字列は参照として受け取ります。",
        "許す cipher の一覧は 2026-09-30 の判断です。後で弱くなった場合は profile の版を上げて直します。",
        "構成が規則に合うことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のサーバーやクラウドの TLS 設定の雛形ではありません。"
      ],
      "integration": "src/tls-config.js の verify(config) を呼びます（投げません）。受け取るのは、min_version・max_version・ciphers_tls12・reject_unauthorized・verify_hostname・ca_refs・client_auth・alpn の 8 つのキーだけの素のオブジェクトです。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code, path }, ...] }（code は error_taxonomy、path は JSON Pointer。違反は規則の順にすべて並ぶ）。実際の設定の文字列にするのは使う側です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-05",
      "category": "B/暗号・信頼",
      "kind": "Tsugite",
      "title": "鍵管理・回転 — 鍵管理・回転の継手（Tsugite）",
      "summary": "鍵管理・回転の継手（Tsugite）は、外の KMS・HSM へ鍵の操作（鍵を作る・新しい版を作る＝回転・primary を替える・版を無効にする・有効に戻す・破棄を予約する・予約を取り消す・様子を見る）の要求（KeyLifecycleRequest）を検めてから運び、返った receipt（KeyLifecycleReceipt）を契約どおりに照合する継手（つぎて）です。鍵の中身は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "鍵の版の無効化や破棄の予約を、人・組織の権限の証拠なしに送ってしまうこと、ENABLED でない版を primary にしてしまうこと、短すぎる猶予で破棄を予約してしまうこと、そして鍵の操作が通ったことを鍵を使う許可と取り違えることを防ぎます。",
      "use_when": [
        "外の KMS・HSM で鍵を作り、新しい版を作って（回転）primary を替える操作を、契約どおりに検めてから運びたいとき。",
        "版の無効化と破棄の予約を、その鍵・版・op に結ばれた人・組織の権限の証拠を受け取ったときだけ送りたいとき。証拠は 1 回の変更で使い切ります。",
        "破棄を、すぐにではなく猶予（1 日以上、設定の下限以上）を置いた予約としてだけ行い、CANCEL_DESTROY で DISABLED に戻せるようにしたいとき。",
        "primary にする版を、この継手が ENABLED と照合した版だけに限りたいとき。"
      ],
      "do_not_use_when": [
        "鍵をすぐに破棄したいとき。すぐに破棄する op は契約にありません。",
        "回転の方針（いつ回すか・何世代残すか）を検めたいとき。これは手元の Primitive の役目で、この継手には入っていません。rotation_policy_ref は運ぶだけです。",
        "鍵を使って暗号化・署名したいとき。暗号化は KMS envelope の継手（B-03 では部品(Component) がこれを束ねます）、署名は B-01 KMS 署名の継手の役目です。",
        "鍵の中身（公開鍵を含む）を受け渡したいとき。この継手は鍵の中身を運ばず、公開鍵も参照だけです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える KMS・HSM への接続がほしいとき。実の KMS・HSM には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "設定を検めます。設定が無い、一覧に無い KMS・鍵の仕様、下限（min_destroy_window_seconds）の無い破棄の予約、下限や 1 日より短い猶予は送りません。",
        "権限の証拠を検めます。証拠の欄が無い、証拠を受け取っていない、出どころが違う（HUMAN・ORGANIZATION でないものを含む）、ほかの鍵・版・op の subject digest、使い切った証拠では、無効化・破棄の予約を送りません。",
        "primary の替え先を検めます。知らない版・ENABLED でない版、食い違う版の記録があれば SET_PRIMARY_VERSION を送りません。",
        "参照と要求の形を検めます。ドライブ・..・制御文字・行区切り・空白や PEM を含む参照、Ref でない request_id、契約に無い op（DESTROY を含む）、op が使わない欄、op ごとの必須の欄の欠けは送りません。",
        "receipt を照合します。request_digest の偽り、別の request_id・KMS・鍵・版、op の食い違い、表の外の状態、効果を増やす・不明な効果、断ったのに効果がある receipt は受理せず、停止します（HALTED）。",
        "鍵の中身・公開鍵そのものを載せた receipt、鍵を使う許可（authorization）を導いた receipt は受理しません。",
        "破棄の時刻を検めます。猶予より手前・過去の破棄の時刻、時刻の無い予約、DESTROY_SCHEDULED でないのに時刻がある receipt、DISABLED に戻らない CANCEL_DESTROY は受理しません。",
        "結果の分からない送信は同じ要求の再送（lookup）で解き、解けるまでほかは送りません。同じ request_id で別の中身の要求は送りません。"
      ],
      "what_it_does_not_do": [
        "権限の証拠を作りません。版の無効化と破棄の予約には、部品の外の人・組織の証拠が入力として要ります（HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_KEY_DISABLE_AND_DESTROY）。証拠の中身（誰が出したか・期限・本物か）も読みません。",
        "鍵の操作が通っても、鍵を使う許可でも業務の承認でもありません（authorization は NOT_DERIVED、approval: false）。",
        "鍵の中身を保管しません。この継手は鍵の操作の要求を運ぶ継手で、鍵の中身は運ばず・持たず、回転の方針を決める機能も含みません。",
        "KMS 側の破棄の猶予が要求より短いか、予約どおりに破棄されたか、取り消しが効いたかは、KMS の答え（DESCRIBE）でしか知りません。",
        "参照の欄が鍵の中身でないことは、形でしか確かめられません。短い符号化された鍵の文字列を参照と見分けることはできません。",
        "版の状態の記録はこの継手の中だけにあり、再起動で消えます。ほかの者が KMS で版の状態を変えると記録は古くなります。資格情報は port 側が持ちます（HELD_BY_PORT）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。",
        "特定のクラウド KMS・HSM の鍵管理 API の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/key-lifecycle/v1 でつなぎます。createKeyLifecycleClient({ cfg, port }) の submit(request) に KeyLifecycleRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。無効化・破棄の予約の前に recordAuthorityEvidence で証拠を渡します。利用側が用意するもの: KMS・HSM への port（submit、任意で lookup）。副作用: 破棄の予約は EXTERNAL_IRREVERSIBLE、DESCRIBE は NONE、ほかは EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-06",
      "category": "B/暗号・信頼",
      "kind": "Tsugite",
      "title": "信頼タイムスタンプ（RFC3161） — タイムスタンプ継手（Tsugite）",
      "summary": "タイムスタンプ継手（Tsugite）は、時刻認証局（TSA）から返った時刻トークンの欄を、こちらが頼んだ内容（ハッシュ・nonce）と pin した TSA・時刻のずれ・精度の上限に照らして検め、一つでも合わなければ信頼しない継手（つぎて）です（fail-closed）。RFC 3161 の DER の解析はせず、署名の検証は 6 つの欄の JSON に対して行います。",
      "problem": "契約や検査で「いつ存在したか」を示すとき、別のハッシュに差し替えられた時刻トークン、使い回し（リプレイ）されたトークン、pin していない TSA のトークン、ずれや精度が大きすぎる時刻を、そのまま信じてしまうことを防ぎます。返ってきた時刻を検めてから来歴へ継ぐための検めです。",
      "use_when": [
        "TSA から受け取った時刻トークンが、こちらの送ったハッシュ（message_imprint）と nonce に一致するかを必ず確かめたいとき。",
        "使う TSA を pin し、時刻のずれ（max_skew_seconds）と精度（max_accuracy_seconds）の上限を決めて検めたいとき。",
        "トークンの欄の署名を、pin した公開鍵（PEM）で検証したいとき（署名は DER 形式・sha256）。",
        "「いつ存在したか」を来歴の核（provenance-facts）へ継ぎたいとき。"
      ],
      "do_not_use_when": [
        "TSA が返す RFC 3161 の DER（ASN.1）形式のトークンを、そのまま渡して検めたいとき。この継手は DER を解析しません。トークンは 6 つの欄を取り出した形で渡す必要があり、署名の検証の対象も、その 6 つの欄の JSON です。",
        "TSA へのタイムスタンプの依頼や、TSA の証明書の取得・pin までまかせたいとき。TSA への通信は含みません（port は無し）。",
        "時刻トークンを発行したいとき。src/tsa.js の signToken は試験用の TSA の代役で、本物の TSA ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "imprint を検めます。トークンの message_imprint が、こちらの送ったハッシュと一致しなければ（差し替え）信頼しません。",
        "nonce を検めます。こちらの nonce がそのまま返っていなければ（リプレイ）信頼しません。",
        "TSA を検めます。pin していない TSA（tsa_id）のトークンは信頼しません。",
        "時刻を検めます。基準の時刻とのずれが上限を超えるもの、精度（accuracy_seconds）が上限を超えるもの、gen_time が整数でないものは信頼しません。",
        "ハッシュのアルゴリズムを検めます。採択していないアルゴリズムは信頼しません。",
        "署名を検めます。verifyToken は、渡された signature_valid が真でなければ信頼しません。verifyTokenSignature は、改ざんされたトークンや別の鍵の署名を、pin した公開鍵で検証できないものとして偽を返します。"
      ],
      "what_it_does_not_do": [
        "RFC 3161 の DER（ASN.1）形式の解析はしません。署名の対象は、トークンの 6 つの欄（message_imprint・hash_algo・tsa_id・nonce・gen_time・accuracy_seconds）の JSON です。",
        "実 TSA への通信と、TSA の証明書の取得・pin はしません。配備のときに決めます。",
        "verifyToken は署名そのものを検証しません。signature_valid（真偽値）を受け取って判定します。署名の検証は verifyTokenSignature で別に行います。",
        "試験では ECDSA P-256 の鍵と試験用の TSA の代役（signToken）を使っています。実 TSA は叩いていません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "TSA API の使い方の雛形ではありません。"
      ],
      "integration": "src/guards.js の verifyToken(cfg, request, token, refTime) に、request { hash, nonce, hash_algo }、token { message_imprint, hash_algo, tsa_id, nonce, gen_time, accuracy_seconds, signature_valid }、基準の時刻（秒）を渡すと { ok, errors } を返します。src/tsa.js の verifyTokenSignature(pinnedPubPem, fields, derSignature) は 6 つの欄の署名を検証し真偽値を返します。含まないもの: RFC 3161 の DER の解析、TSA への通信、TSA の証明書の取得と pin（配備のときに決めます）。port は無し。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-07",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "ハッシュ・内容アドレス — ハッシュ・内容アドレスの部品（Primitive）",
      "summary": "ハッシュ・内容アドレスの部品（Primitive）は、bytes の sha256、契約で使う型つきの digest（domain・algorithm・value・target）、内容アドレス（sha256:<hex>）を作り、照合する単独の部品です。受け取るのは bytes だけです。照合は形・アルゴリズム・値（定数時間の比較）・期待した domain と target を検め、{ ok, errors } を返して例外を投げません（fail-closed）。",
      "problem": "provenance・reconcile の土台として内容から一意の指紋を作り、照合するときに、bytes と文字列を取り違えること、形の違う digest や別のアルゴリズムを受け取ってしまうこと、domain・target を取り違えて照合してしまうことを防ぎます。",
      "use_when": [
        "bytes から sha256（小文字 64 桁の hex）や内容アドレス（sha256:<hex>）を作りたいとき。",
        "契約で使う型つきの digest（{ domain, algorithm: \"sha256\", value, target }）を作り、照合したいとき。domain と target は名札で、ハッシュには混ぜません。",
        "digest を bytes と照合するときに、期待した domain・target を渡して取り違えを止めたいとき。",
        "例外を投げない照合が要るとき。verifyDigest と parseContentAddress は { ok, errors } を返し、例外を投げません。",
        "JSON の値の digest を取りたいとき。I-08 の部品で正準の bytes にしてから、この部品に渡します。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。"
      ],
      "do_not_use_when": [
        "文字列をそのまま渡したいとき。この部品は文字列を受け取りません（NOT_BYTES）。文字列は、使う側が決まった文字コード（UTF-8 など）で bytes にしてから渡します。",
        "digest の一致で、出どころを証明したいとき。一致は出どころの証明ではありません。署名の検証は B-02 です。",
        "sha256 のほかのアルゴリズムが要るとき。",
        "bytes の大きさの上限が要るとき。上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "bytes（Uint8Array）でないとき（文字列・数・配列・ArrayBuffer・ほかの型付き配列）、NOT_BYTES で止めます。",
        "domain の形が違うとき INVALID_DOMAIN、target が空・文字列でない・1024 文字を超えるとき INVALID_TARGET で止めます。",
        "digest が 4 つのキーだけのオブジェクトでないとき（キーの余り・欠け・digest が無い）、NOT_A_DIGEST で止めます。",
        "algorithm が sha256 でないとき UNSUPPORTED_ALGORITHM、value が小文字 64 桁の hex でないとき（大文字・長さ違い）MALFORMED_VALUE で止めます。",
        "bytes の sha256 が value と違うとき、定数時間の比較（crypto.timingSafeEqual）で VALUE_MISMATCH を返します。",
        "期待した domain・target と違うとき DOMAIN_MISMATCH・TARGET_MISMATCH、expect がオブジェクトでないとき INVALID_EXPECTATION で止めます。",
        "内容アドレスが sha256:<小文字 64 桁の hex> でないとき、MALFORMED_CONTENT_ADDRESS で止めます。"
      ],
      "what_it_does_not_do": [
        "文字列の bytes 化・JSON の正準化はしません。文字列は使う側が bytes にし、JSON の値は I-08 の部品で正準の bytes にしてから渡します。",
        "digest の一致は出どころの証明ではありません。署名の検証（B-02）や鍵の管理は含みません。",
        "digest が一致したことは、中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "sha256 のほかのアルゴリズムは扱いません。",
        "継手の中にある digests.js の写し（19 個）を、まだこの部品に置き換えていません。bytes では写しの sha256hex と byte 単位で同じ値ですが、写しは文字列も受け取って UTF-8 としてハッシュし、この部品は bytes だけを受け取ります。置き換えは互換表を確かめた後の別の作業です。",
        "要求の正準化＋sha256（requestDigestValue）と、どのフィールドを digest に入れるかは決めません。使う側（各継手・契約）に残ります。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のハッシュのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/digest.js の verifyDigest(digest, bytes, expect) を呼びます（投げません）。受け取るのは、4 つのキーだけの digest、bytes（Uint8Array。Buffer を含む）、任意の expect（{ domain, target }）です。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code }] }（code は error_taxonomy）。sha256Hex・digest・contentAddress は受け取れない値で DigestError を投げ、parseContentAddress は投げません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-08",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "乱数・nonce（CSPRNG） — 乱数・nonce の部品（Primitive）",
      "summary": "乱数・nonce の部品（Primitive）は、OS の CSPRNG（Node.js の crypto.randomBytes）の上の薄い包みです。randomBytes(n)（1〜1024 bytes）、nonce（hex か base64url）、randomInt(maxExclusive)（1〜2^48 の範囲で一様）を作ります。受け持つのは引数の検め・出力の形・randomInt の棄却法だけで、乱数の質そのものは OS と Node.js の crypto に任せます。",
      "problem": "「弱いと全部崩れる裏方」である乱数の作り方を固定します。出どころを OS の CSPRNG に固定して差し替えられないようにし、使う側の seed を受け取らず、長さ・符号化・範囲を検め、randomInt は x mod m の偏りを作らない棄却法で一様にします。出どころが失敗したときは値を作らずに止めます（fail-closed）。",
      "use_when": [
        "OS の CSPRNG から、決まった長さ（1〜1024 bytes）の bytes を引きたいとき。",
        "nonce を小文字の hex か、パディングなしの base64url（RFC 4648 の URL と名前に安全な字母）の文字列で作りたいとき。",
        "0 以上 max_exclusive 未満の整数を、1〜2^48 の範囲（2^48 を含む）で偏りなく作りたいとき。",
        "使う側の seed が混じらないことを入口で守りたいとき。seed のフィールドは UNKNOWN_FIELD で断ります。",
        "例外を投げない呼び方が要るとき。generate(request) は { ok, errors, value } を返し、例外を投げません。"
      ],
      "do_not_use_when": [
        "乱数の質（予測できないこと・偏りのなさ）の確かめを求めるとき。この部品は確かめておらず、OS と Node.js の crypto.randomBytes に任せています。",
        "用途に足りる長さの判断を任せたいとき。長さは使う側が決め、1 byte の nonce も受け取ります。",
        "再現できる（決定的な）値が要るとき。値は呼ぶたびに OS の CSPRNG から引き、再現できません（設計どおり）。",
        "1 回に 1024 bytes を超える bytes や、2^48 を超える範囲の整数、bigint・文字列の数を扱いたいとき。",
        "fork したプロセス・仮想マシンの複製などで使うとき。そこでの OS の CSPRNG の振る舞いは確かめていません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "要求が素のオブジェクトでないとき（配列・Map・クラスのインスタンスなど）、NOT_A_REQUEST で止めます。",
        "kind が \"bytes\"・\"nonce\"・\"int\" のどれでもないとき（プロトタイプの名前を含む）UNKNOWN_KIND、決まったフィールドのほか（seed を含む）を持つとき UNKNOWN_FIELD で止めます。",
        "length・bytes が 1〜1024 の整数（number）でないとき（0・1024 超え・小数・文字列・bigint）、INVALID_LENGTH で止めます。",
        "encoding が \"hex\" でも \"base64url\" でもないとき INVALID_ENCODING、max_exclusive が 1〜2^48 の整数（number）でないとき INVALID_BOUND で止めます。",
        "乱数の出どころが投げた・長さの違う値や bytes でない値を返したとき、または randomInt で 64 回続けて捨てたとき、値を作らず ENTROPY_UNAVAILABLE で止めます。",
        "要求を調べること自体が例外になるとき（getter・Proxy の罠など）、generate は INSPECTION_FAILED を返します。フィールドは一度だけ読み、検めた値だけを使います。"
      ],
      "what_it_does_not_do": [
        "「予測不能な値の生成」を名前に持つ項目ですが、この部品は乱数の質（予測できないこと・偏りのなさ）を確かめていません。OS と Node.js の crypto.randomBytes に任せています。統計の試験は大きな偏りを見る煙の試験で、NIST SP 800-22 などの正式な統計試験ではありません。",
        "どの用途に何 bytes 使うか（nonce の一意性・トークンの強さ）は判断しません。使う側が決めます。",
        "出どころの失敗（ENTROPY_UNAVAILABLE）は、差し替えた出どころで確かめました。実際の OS の CSPRNG を失敗させては確かめていません。",
        "使う側の seed や、出どころを差し替える手段は受け取りません。公開の入口の module.exports は凍結してあり、内部の makeRandom は出していません。",
        "値ができたことは、使う側の目的に長さが足りることでも、乱数の質が正式な試験で確かめられたことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の乱数のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/random.js の generate(request) を呼びます（投げません）。受け取るのは { kind: \"bytes\", length }・{ kind: \"nonce\", bytes, encoding }・{ kind: \"int\", max_exclusive } のどれかです。返すのは { ok: true, errors: [], value }（Buffer・文字列・整数）か { ok: false, errors: [{ code }] }（code は error_taxonomy）。randomBytes・nonce・randomInt は受け取れないとき RandomError を投げます。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto と util だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-09",
      "category": "B/暗号・信頼",
      "kind": "Tsugite",
      "title": "証明書発行・更新（ACME） — 証明書の発行・更新（ACME）の継手（Tsugite）",
      "summary": "証明書の発行・更新（ACME）の継手（Tsugite）は、ACME（RFC 8555）の認証局へ、注文（NEW_ORDER。更新は renewal_of_ref つきの新しい注文）・challenge の一覧・応答の準備ができたことの知らせ・注文の状態の確かめ・発行の依頼（FINALIZE）・証明書の取り出しの要求（AcmeRequest）を検めてから運び、返った receipt（AcmeReceipt）を照合する継手（つぎて）です。発行できたことを、名前の正当な所有や信頼として扱いません（fail-closed）。",
      "problem": "取り消せない発行（FINALIZE）を、注文と名前の合わない CSR や、人・組織の根拠なしに依頼してしまうこと、そして名前や公開鍵の違う証明書を受け取ったまま使ってしまうことを防ぎます。発行は Certificate Transparency に公に記録されるため、送る前と受け取った後の両方で検めます。",
      "use_when": [
        "TLS 証明書を ACME の認証局から取得し、更新（renewal_of_ref つきの新しい注文）するときの要求を、契約どおりに検めてから運びたいとき。",
        "発行の依頼を、この directory・注文・CSR・名前の主体（authority_subject_digest）に結ばれた人・組織の根拠を受け取ったときだけ送りたいとき。",
        "CSR の名前が注文の名前とちょうど同じで、CSR の中身と公開鍵の sha256 が要求の digest と同じことを確かめてから発行を依頼したいとき。",
        "取り出した証明書の名前と公開鍵を、連鎖を読み戻して注文・CSR と照合してから受け取りたいとき。"
      ],
      "do_not_use_when": [
        "証明書を失効させたいとき。失効（REVOKE）は v1 の契約に無く、別の判断が要ります。",
        "challenge の応答（HTTP のファイル・DNS の記録）を置きたいとき。それは別の port の役目で、この継手は challenge の参照と digest を返すだけです。",
        "更新の時期や期限切れを判断させたいとき。この継手は有効期間の形と前後を検めるだけで、その判断はしません。",
        "TLS の構成を検めたいとき。それは手元の Primitive（B-04）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える認証局への接続がほしいとき。実の認証局には一度も接続しておらず、port の実装・account 鍵の結び付け（JWS の署名）・nonce・設定の値は接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "設定を検めます。設定が無い・一覧が空、一覧に無い directory・account 鍵・名前、名前を覆わないワイルドカード、DNS でない・大文字・重なる・空・昇順でない名前の注文は送りません。",
        "発行の根拠を検めます。根拠を受け取っていない、出どころが違う（HUMAN・ORGANIZATION でないものを含む）、別の注文・CSR・名前・名前の順に結ばれた根拠では、FINALIZE を送りません。",
        "CSR を検めます。CSR を読めない、中身や公開鍵の sha256 が digest と違う、名前の欠け・余り・違い、注文の外の CN、SAN や extensionRequest が二つある CSR では、FINALIZE を送りません。",
        "注文の順番を検めます。知らない注文、directory や account 鍵の変わった注文、pending でない注文・challenge への知らせ、ready でない注文の FINALIZE、valid でない・この継手が FINALIZE していない注文の取り出しは送りません。",
        "参照と要求の形を検めます。..・ドライブ・制御文字・行区切りを含む参照、空白を含む account 鍵の参照、account 鍵の秘密鍵の欄、REVOKE、ほかの op の欄、Ref でない request_id は送りません。",
        "receipt を照合します。request_digest の偽り、別の op・directory・注文、名前の所有や信頼を導いた receipt、効果を増やす・小さく言う・不明な効果、注文や challenge の状態の後戻りは受理せず、停止します（HALTED）。",
        "証明書を照合します。leaf_names の欠け・余り・違い、CSR と違う公開鍵、連鎖を読み戻せない・sha256 が違う、有効期間の違い、名乗りと読み戻した leaf の食い違いがあれば、照合済みにしません。",
        "DER の読み取りで、最小でない長さ・高いタグ番号・後ろに余るバイトを受け付けません。"
      ],
      "what_it_does_not_do": [
        "発行の根拠を作りません。FINALIZE には、部品の外の人・組織の根拠が入力として要ります（HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_CERTIFICATE_ISSUANCE）。根拠の記録が本当に人・組織から来たかも確かめません。",
        "発行できた・取り出せたことは、名前の正当な所有でも信頼でもありません（name_ownership・trust_decision は NOT_DERIVED、approval: false）。ドメインを持っていることは確かめません。",
        "証明書の自動の取得・更新を自分で回しません。この継手は ACME の要求を運ぶ継手で、更新の時期の判断、challenge の応答を置くこと、失効（REVOKE）の機能は含みません。",
        "CSR の自己署名と、証明書の署名・連鎖・失効・信頼は検めません。leaf は連鎖の先頭の証明書とみなします。",
        "CSR と証明書は、要る DER だけを読む小さな読み取りで、試験は手元で組み立てた P-256 の CSR と証明書だけです。実の認証局が出した証明書では確かめていません。認証局が公開鍵の書き方を変えると、同じ鍵でも照合済みになりません。",
        "注文の状態と受け取った根拠はこの継手の中だけにあり、再起動で消えます。名前は小文字の ASCII だけを扱います。account 鍵などの資格情報は port 側が持ちます（HELD_BY_PORT）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。",
        "特定の認証局の ACME クライアントの雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/acme-certificate/v1 でつなぎます。createAcmeClient({ cfg, port }) の submit(request, csr) に AcmeRequest を渡すと（csr は FINALIZE のときだけ）、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。FINALIZE の前に recordAuthorityEvidence で根拠を渡します。利用側が用意するもの: 認証局への port（submit・readChain、任意で lookup）。副作用: FINALIZE は EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-11",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "Merkle 木（根・包含証明） — Merkle 木（根・包含証明）の部品（Primitive）",
      "summary": "Merkle 木（根・包含証明）の部品（Primitive）は、bytes の葉の並びから、RFC 9162 の 2.1 節（Certificate Transparency v2）の Merkle Tree Hash（SHA-256）で根を作り、包含証明と一貫性証明を作って検める単独の部品です。どの操作も投げずに { ok, errors } を返し、形の悪い入力は決まったコードで止め、照合の不一致は verified: false（エラーではない）で返します。",
      "problem": "「束ねて署名した」の中身を、後から照らす土台になります。束ねた木の根に署名しておけば、ある葉が含まれていることを包含証明で部分的に確かめられ、追記した後の木が前の木を先頭に持つことを一貫性証明で確かめられます。葉と節の前置き（0x00・0x01）で、葉と節を取り違えた偽の葉を防ぎます。",
      "use_when": [
        "bytes の葉の並びから、RFC 9162 の木の根（小文字 64 桁の hex）を作りたいとき（merkleRoot）。",
        "ある葉が根に含まれることを示す包含証明（audit_path）を作り、検めたいとき（inclusionProof・verifyInclusion）。",
        "追記で大きくなる記録について、古い木が新しい木の先頭であることを示す一貫性証明を作り、検めたいとき（consistencyProof・verifyConsistency）。",
        "照合の不一致（証明・葉・番号・根の取り違え、足りない・余る証明）を、例外ではなく verified: false で受け取りたいとき。",
        "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。依存パッケージはありません。"
      ],
      "do_not_use_when": [
        "透明性ログが要るとき。この部品は木を保存・追記せず、署名つきの木の頭（STH）・ログの監視・gossip・ログの ID を扱いません。",
        "証明だけで大きいほうの木の大きさを決めたいとき。証明の通り道で形が同じ別の大きさでも true になることがあります。大きさと根は、署名された記録から取ってください。",
        "SHA-256 のほかのハッシュが要るとき、または 1,048,576 葉より大きな木・ログの証明を検めたいとき（BAD_SIZE）。",
        "old_size が 0 の一貫性や、大きさの同じ木（old_size = new_size）の一貫性を確かめたいとき。BAD_SIZE で止めます。同じ大きさなら、使う側が根を直接比べます。",
        "奇数のときに右端を複製する形（Bitcoin 型）の木が要るとき。この部品は複製せず、右端をそのまま上の段へ上げます。",
        "大きな木で証明を何度も作る使い方の速さが要るとき。証明を作るたびに渡された葉から木を作り直し（O(n)）、その速さは測っていません。葉の合計の bytes の上限も持たず、メモリーと時間は呼ぶ側の持ち分です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "leaves が配列でない、要素が bytes（Uint8Array）でない（穴・getter・ほかの型付き配列・ArrayBuffer・DataView・Proxy で包んだ bytes を含む）、引数がオブジェクトでないとき、NOT_BYTES_LIST で止めます。",
        "葉が 1,048,576 個を超えるときは要素を読む前に TOO_MANY_LEAVES、葉 1 つが 64 MiB を超えるときはハッシュの前に LEAF_TOO_LARGE で止めます。",
        "index・leaf_index が整数でない・負・木の大きさ以上のとき、INDEX_OUT_OF_RANGE で止めます。verifyInclusion でも、照合の失敗ではなくエラーとする厳しいほうの読みです。",
        "tree_size が 1〜1,048,576 の整数でないとき、old_size・new_size が 0 < old_size < new_size ≤ 1,048,576 の整数でないとき、BAD_SIZE で止めます。",
        "root・old_root・new_root と証明の要素が小文字 64 桁の hex の文字列でないとき BAD_HEX、audit_path・proof が配列でないとき BAD_PROOF_SHAPE で止めます。",
        "証明の要素の書き換え・抜け・追加・入れ替え、葉・番号・根の取り違えは通しません（verified: false）。作った証明は、同じ根と大きさで必ず true になります。",
        "止めるときは、決まった順で最初に見つけた 1 つのコードだけを返します。getter を呼ばず、調べると例外になる値（Proxy の罠など）でも投げません。"
      ],
      "what_it_does_not_do": [
        "署名はしません（根への署名は B-12）。時刻の証明もしません（B-06）。根が誰のものか・いつのものかは言いません。",
        "verified: true は、SHA-256 の衝突が無い限り、渡された葉が渡された根と大きさの木に含まれる（または古い木が新しい木の先頭である）と言えるというだけです。根が正しいこと、中身が正しいこと、受け入れてよいことは示しません。",
        "木・証明・記録を保存しません。呼び出しの間に状態を持たず、記録の保存と追記の管理は使う側（H-11 など）に残ります。",
        "比較は定数時間ではありません（根と証明は秘密ではない前提）。強さは SHA-256 の衝突・原像の困難さに頼ります。",
        "certificate-transparency の試験の 1〜8 葉の根は試験に書き写した値で、元のファイルを取り寄せて照合してはいません（定義から手で組んだ値と一致することで確かめました）。",
        "根・証明・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の透明性ログ（CT のログ・Trillian など）の雛形ではありません。"
      ],
      "integration": "src/merkle.js の merkleRoot・inclusionProof・verifyInclusion・consistencyProof・verifyConsistency を呼びます（どれも投げません）。引数は leaves・index・leaf・leaf_index・tree_size・audit_path・root・old_size・new_size・old_root・new_root・proof を持つオブジェクトで、葉は Uint8Array、根と証明の要素は小文字 64 桁の hex です。返すのは { ok: true, errors: [], ... } か { ok: false, errors: [{ code }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-12",
      "category": "B/暗号・信頼",
      "kind": "Primitive",
      "title": "手元の鍵での署名（Ed25519） — 手元の鍵での署名（Ed25519）の部品（Primitive）",
      "summary": "手元の鍵での署名（Ed25519）の部品（Primitive）は、呼ぶ側が持つ Ed25519 の秘密鍵（Node.js の KeyObject）で bytes に署名し、署名（詰め物なしの base64url）と、導いた公開鍵の SPKI DER の sha256（指紋）を返す単独の部品です。秘密鍵は KeyObject でだけ受け取り、書き出さず・保管せず・返しません。どの操作も投げずに { ok, errors } で返します（fail-closed）。",
      "problem": "記録への署名の裏で、鍵をどう扱うかは見えません。この部品は、bytes・PEM・JWK などで秘密鍵を持ち回る使い方を断り、秘密鍵を KeyObject でだけ受け取ります。形の違う鍵・署名は決まったコードで止め、止めるときは署名を返しません。",
      "use_when": [
        "呼ぶ側が持つ Ed25519 の秘密鍵で、bytes（16 MiB まで）に署名したいとき（sign）。何に署名するか（Merkle 木の根や正準の bytes など）は使う側が決めます。",
        "どの鍵で署名したかを、公開鍵の SPKI DER の sha256（public_key_spki_sha256）で示したいとき（sign・publicKeyFingerprint）。",
        "署名した直後に往復の確かめをしたいとき（verify。違えば verified: false で、エラーではない）。",
        "同じ鍵と同じ message から、いつも同じ署名が要るとき（RFC 8032 の Ed25519 の pure。時計・乱数を使わない）。",
        "KMS・HSM を使わず、手元の鍵で署名したいとき。この部品は KMS・HSM・鍵の保管庫とは話しません。"
      ],
      "do_not_use_when": [
        "使う側が pin した鍵で署名を検証したいとき。verify は往復の確かめで、その代わりではありません。pin した鍵での検証は B-02 です。",
        "鍵の保管・入れ替え・失効や、HSM・KMS での署名が要るとき。この部品は扱いません。",
        "秘密鍵を bytes・文字列・PEM・JWK・WebCrypto の CryptoKey で渡したいとき。KEY_NOT_KEYOBJECT で止めます。",
        "Ed25519 のほかの種類の鍵（EC・RSA・X25519・Ed448・共通鍵）で署名したいとき。KEY_TYPE_UNSUPPORTED で止めます。",
        "16 MiB を超える message に署名したいとき。MESSAGE_TOO_LARGE で止めます。",
        "サイドチャネル（時間・電力）への強さが要るとき。確かめていません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が素のオブジェクトでない、または決まった欄のほかを持つとき、INVALID_ARGUMENT で止めます。引数を読むこと自体が例外になるとき（getter や Proxy の罠）は INSPECTION_FAILED です。",
        "message が Uint8Array でないとき NOT_BYTES、16 MiB を超えるとき MESSAGE_TOO_LARGE で止めます。",
        "鍵が KeyObject でないとき（PEM・DER・JWK・bytes・CryptoKey・KeyObject を装ったもの・KeyObject の Proxy・欄が無い）、KEY_NOT_KEYOBJECT で止めます。",
        "KeyObject でも種類が違うとき（sign に公開鍵、verify に秘密鍵、共通鍵・EC・RSA・X25519・Ed448）、KEY_TYPE_UNSUPPORTED で止めます。",
        "key_id が Ref（印字できる ASCII の空白を除く 1〜200 文字、\"..\" を含まず、/ と \\ で始まらない）でないとき、INVALID_KEY_ID で止めます。",
        "signature が詰め物なしの正準の base64url で 64 bytes の文字列でないとき（詰め物・標準の base64・余りの bits が 0 でない・長さ違い）、INVALID_SIGNATURE で止めます。",
        "署名の 1 bit の反転、S + L の署名、ほかの message・ほかの鍵の署名は verified: false になります。",
        "引数の欄はそれぞれ一度だけ読み、getter が違う値を返しても、検めた値だけを使います。"
      ],
      "what_it_does_not_do": [
        "署名は「この秘密鍵を持つ誰かがこの bytes に署名した」ことだけを示します。承認・中身が正しいこと・受け入れてよいこと・署名者が誰かの証明ではありません。誰が署名してよいかの判断は、この部品の外です。",
        "この部品は秘密鍵を書き出さず（export を呼ばない）、保管せず、ログに出さず、結果に入れません。ただし、呼ぶ側が渡した KeyObject を消すことはしません（消すのは GC 任せ）。",
        "key_id は呼ぶ側の名札で、鍵と結びついているかを確かめません。どの鍵かを確かめるには public_key_spki_sha256 を使います。",
        "Ed25519 の計算そのものは Node.js の crypto（OpenSSL）に任せています。確かめたのは RFC 8032 の 7.1 の TEST 1〜3 と、実行時に作った鍵での Node の crypto との突き合わせまでです。",
        "verify の厳しさ（小さい位数の公開鍵・正準でない点など）は OpenSSL に従い、S + L の署名のほかの境目は確かめていません。B-02 との突き合わせもしていません。",
        "署名・指紋・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の署名ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/local-sign.js の sign({ message, private_key, key_id })・publicKeyFingerprint({ public_key })・verify({ message, public_key, signature }) を呼びます（どれも投げません）。sign は { ok: true, errors: [], algorithm: \"Ed25519\", key_id, signature, public_key_spki_sha256 } を返します（signature は 86 文字の base64url）。止めるときは { ok: false, errors: [{ code }] }。KeyObject は呼ぶ側が作って持ちます。副作用・状態・外の接続は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "B-13",
      "category": "B/暗号・信頼",
      "kind": "Adopted",
      "title": "重ね署名の確かめ（.amupkg）の受け入れ部品（Adopted）",
      "summary": "重ね署名の確かめ（.amupkg）の受け入れ部品（Adopted）は、.amupkg を Runtime に渡す前に、目録・自分用の署名・wi-t.com の重ね署名を、固定した起点（root-usb-1）の trust bundle の鍵と失効で確かめ、PASS か REFUSE を返す部品です。別の書き手が作った部品を、元の版（33f2b1c）の bytes を書き換えずに受け入れたもので、ERABAZU が足したのはこの説明書と工房のファズだけです。",
      "problem": "「確かめ済み」の裏で、どの鍵と失効を見たかは見えません。この部品は、重ね署名が無い・正しくないものを REFUSE にして通さず（fail-closed）、本物の鍵・取得先・起点を部品の中に固定して引数で差し替えさせず、trust の巻き戻りや同じ generation の別の bytes を断ります。結果には、見た trust と目録の digest を facts として添えます。",
      "use_when": [
        ".amupkg の bytes（Uint8Array）を Runtime に渡す前に、完全性・自分用の署名・重ね署名・trust・失効・結び付けを確かめ、verdict（PASS か REFUSE）・code・stage・facts を受け取りたいとき（verifyAmupkg。投げません）。",
        "PASS の後に Runtime で開いたとき、返った amupkg_digest が sha256: ＋ facts.manifest_digest と同じかを照らすための値を受け取りたいとき。この照らし合わせは使う側が行います。",
        "trust bundle を取りに行き、確かめに通ったものだけを store の port に保存したいとき（refreshTrust）。取れないときは、保存したものか、部品に焼き込んだ gen 10 で判定を続けます。",
        "trust の鮮度を区別したいとき。新しい受け入れは trust の next_update まで、受け入れ済みのものは next_update から 30 日まで動かし、過ぎれば COUNTERSIG_TRUST_STALE・COUNTERSIG_TRUST_EXPIRED で止めます。",
        "PASS したものだけを受け入れの記録に残し（recordAdmitted）、trust・受け入れた数・確かめの版を見たいとき（status）。",
        "どの元の版の bytes を受け入れたかを、元の commit（33f2b1c）と 36 本のファイルの sha256 で照らせる部品が要るとき。"
      ],
      "do_not_use_when": [
        "利用者が持ち込む .amupkg を、固定した見本（pinnedDigests）で通したいとき。固定した見本を守るのは、使う側のプログラムのコード署名で、部品はそれを確かめません。利用者が持ち込む .amupkg には使えません。",
        "本物の鍵で署名した重ね署名が PASS することを、工房で確かめた結果として扱いたいとき。工房はそれを確かめていません（工房の試験とファズは、使い捨ての起点と鍵だけを使います）。",
        "PASS を、承認・販売の許可・中身が正しいことの印として使いたいとき。PASS は「出所・完全性・確認の範囲を確かめた」ことだけです。",
        "ZIP のファイルの bytes をまるごと同じものに限りたいとき。PASS は目録と目録に載ったファイルと署名で決まるので、その用途では facts.archive_digest（ZIP 全体の sha256）を使います。",
        "利用者に見せる文（display の title_ja・detail_ja）を、そのまま本番の文として使いたいとき。display は仮の文です。",
        "元の版が直ったときに、自動で新しい版を使いたいとき。自動では追いません（auto_follow: false）。新しい版は写し直して検査し直します。trust の新しい generation（gen 11 以降）が出たときも検査し直します。",
        "Node.js v24.21.0 のほかの版で使うとき。元は Node.js 20 以上の ES module ですが、工房はほかの版を確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "確かめる順は、完全性 → 署名のファイルが目録に無いこと → 自分用の署名 → 固定した見本 → 重ね署名のファイル → 重ね署名の形 → trust（起点・鮮度）→ 重ね署名の鍵と署名 → 失効 → 結び付け、です。止めたときは、code と stage（integrity・self_signature・countersignature・trust・pinned）を返します。",
        "ZIP が上限（96 MiB・2000 件・1 件 64 MiB・合計 192 MiB・倍率 100）を超える・安全でない名前・読めないとき PKG_ARCHIVE_UNSAFE、載ってよいファイル・区分の決まりに合わないとき AMUPKG_SECTION_INVALID、目録の sha256・区分の digest が合わないとき AMUPKG_DIGEST_MISMATCH、目録の profile が扱えないとき AMUPKG_PROFILE_UNSUPPORTED、署名のファイルが目録に載っているとき MANIFEST_LISTS_SIGNATURE_FILE で止めます。",
        "自分用の署名が無いとき SELF_SIGNATURE_MISSING、目録に合わないとき SELF_SIGNATURE_INVALID、自分用の鍵が失効しているとき SELF_KEY_REVOKED で止めます。",
        "重ね署名のファイルが無い（COUNTERSIG_MISSING）・読めない（COUNTERSIG_UNREADABLE）・schema や purpose が違う・期限がある（期限は持たない決まり）・確認の範囲（scope_id・scope_version）が無い・重ね署名者が wi-t.com でない・この .amupkg の自分用の鍵と署名に結び付いていない（COUNTERSIG_SELF_SIGNER_MISMATCH）とき、それぞれのコードで止めます。",
        "重ね署名の鍵が trust bundle に無い（COUNTERSIGNER_KEY_UNKNOWN）・重ね署名専用でない・方式が違う・失効している、重ね署名の署名が合わない（COUNTERSIG_SIGNATURE_INVALID）、その重ね署名が失効している（COUNTERSIG_REVOKED）とき、REFUSE にします。",
        "trust bundle が確かめられない（取れない・保存も焼き込みも使えない）とき COUNTERSIG_TRUST_UNAVAILABLE で止めます。trust の取得は 1 ファイル 256 KiB・10 秒までで、redirect を追いません。trust の巻き戻りと、同じ generation の別の bytes は断ります。",
        "正しい .amupkg を 1 回ごとに 1 つの壊し方（目録の対象のファイルの書き換え・消し・足し、重ね署名の差し替え、別の鍵での署名の作り直し、ランダムな bytes の書き換えなど）で変えて答えと突き合わせる 1,000,000 回のファズを工房の検査で走らせ、不一致と fail-open が 0 でした（Node.js v24.21.0）。"
      ],
      "what_it_does_not_do": [
        "PASS は「出所・完全性・確認の範囲を確かめた」ことだけで、承認・販売の許可・中身の正しさではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "部品が確かめるのは、目録と、目録に載ったファイルと、署名だけです。ZIP の中のファイルの外にある bytes（末尾など）は確かめの対象外で、Runtime も使いません。ファイルの bytes をまるごと同じにしたいときは facts.archive_digest（末尾に足すと変わる）を使います。",
        "固定した見本（pinnedDigests）を守るのは使う側のプログラムのコード署名で、部品はそれを確かめません。利用者が持ち込む .amupkg には使えません。",
        "本物の trust（gen 10）には重ね署名の鍵 wi-t-countersigner-ed25519.v1 が載っていて、その鍵で署名されていない重ね署名は COUNTERSIG_SIGNATURE_INVALID になります。部品に焼き込んだ trust も gen 10 です。ただし、本物の鍵で署名した重ね署名が PASS することは、工房では確かめていません。工房の試験とファズは本物の秘密鍵を使わず、使い捨ての起点と鍵だけを使います。",
        "これは ERABAZU が作った確かめの実装ではありません。別の書き手が作った部品を、bytes を書き換えずに受け入れたものです（36 本の sha256 と元の出所の記録で照らします）。中身・確かめの順番・止めるコード・API は元の設計のままです。受け入れたファイルには LICENSE・LICENSES/CC0-1.0.md・NOTICE もあり、NOTICE は元の書き手が足したものです。",
        "Runtime の実行ファイルとの通し（flow）は、工房の検査では動かしていません（実行ファイルが無い）。元のリポジトリの CI（run 37588955237）で PASS しています。元の試験は 54 件（pass 53・skip 1）です。",
        "store の port に trust と受け入れの記録を書き、fetch の port で trust の 4 つのファイルを読みます（LOCAL_REVERSIBLE）。部品のメモリーには状態を持たず、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/index.mjs（ES module）の createCountersignatureVerifier({ store, fetch?, now?, log?, pinnedDigests? }) で instance を作り、await instance.verifyAmupkg(bytes, { admit? }) を呼びます。store（read・write）は必須の port、fetch（null なら取りに行かない）・now・log は省けます。返すのは { verdict, code, stage, facts, display } です。factory の引数の形が違うときと、bytes が Uint8Array でないときだけ TypeError を投げます。依存は Node.js の組み込み（node:crypto・WebCrypto）だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": [
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/bytes.mjs",
            "license": "MPL-2.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/canonical-json.mjs",
            "license": "MPL-2.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/purposes.mjs",
            "license": "MPL-2.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/roots.mjs",
            "license": "MPL-2.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-index.mjs",
            "license": "MPL-2.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.sig.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.sig.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.sig.json",
            "license": "CC0-1.0"
          },
          {
            "path": "vendor/kokoroamu-trust-f99fdbe/verify-core.mjs",
            "license": "MPL-2.0"
          }
        ]
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-01",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "オブジェクト保管 — オブジェクト保管継手（Tsugite）",
      "summary": "オブジェクト保管継手（Tsugite）は、ファイルや blob の保存（put）と読み出し（get）を、外部の object backend へ中継する継手（つぎて）です。put は検めた要求だけを送り、backend が返す受領（ObjectReceipt）を契約どおりに照合します。get は返ってきた中身（bytes）の digest を自分で計算し、期待値と一致して受領も照合できたときだけ中身を渡します。一つでも合わなければ受理しません（fail-closed）。",
      "problem": "「保存した」で終わらせてしまうこと、つまり backend の受領や申告をそのまま信じて、化けた中身や偽った保存物を正しいものとして扱ってしまうことを防ぎます。読み出した中身は backend の「一致した」という申告だけでは検証済みにせず、自分で digest を計算して突き合わせます。",
      "use_when": [
        "ファイルや blob を外部の object backend に保存し、読み出したいとき。",
        "読み出した中身を、backend の申告ではなく自分で計算した digest で確かめてから使いたいとき。",
        "受け付ける content-type と大きさの上限を決め、中身の種類を偽ったもの（magic bytes と合わないもの）を通したくないとき。",
        "同じ中身を二重に保存したくないとき、また一度書いたら上書きしない（WORM）対象を守りたいとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
        "部分書込や偽の受領が出たら、人が確かめるまで保存を止めたいとき。"
      ],
      "do_not_use_when": [
        "application/pdf・image/png・image/jpeg・text/plain・application/json 以外の content-type を、中身の種類まで確かめて扱いたいとき。magic bytes で確かめられるのはこの 5 種だけで、確かめられない型は通しません。",
        "保持期間・TTL（C-08）やアーカイブ（C-09）のための削除・ライフサイクルの操作が要るとき。この継手には含まれていません（後続）。",
        "put の成功を「中身が正しいと確かめた」と扱いたいとき。put の成功は中身の検証済みではありません（content_verified: false）。",
        "保存の成功を、承認や決裁として扱いたいとき。どの成功も承認ではありません。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの object store につなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、side effect の条件に反するものは送りません。",
        "pin した backend の版を検めます。pin していない backend の版（backend_profile_revision）への要求は送りません。",
        "中身の種類と大きさを検めます。magic bytes と合わない content-type（種類の偽り）、上限を超える大きさは送りません。",
        "二重保存と上書きを検めます。同じ object_ref・同じ digest は二重に保存せず（ALREADY_STORED）、WORM の対象への上書きは送りません。",
        "get で読み出した中身の digest を自分で計算し、期待値と合わなければ中身を渡しません（GET_REJECTED）。読み出し時の改ざんを拒否します。",
        "受領（ObjectReceipt）を照合します。request_digest を偽った受領、部分書込（MISMATCH）があれば受理せず、以後は停止します（HALTED）。",
        "backend の障害（500 の応答、書いた後の失敗）は UNKNOWN とし、保存済みとして扱いません。再送は照会（lookup）で解きます。",
        "秘密の渡し方を検めます。SecretRef は secret-ref/v2 の場所だけを受け付け、SecretRef の隣に置いた生の秘密や、HUMAN_CREDENTIAL の種類は拒否します。"
      ],
      "what_it_does_not_do": [
        "化けていないかを決めるのは核（integrity-facts）の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "put の成功は中身の検証済みではなく（content_verified: false）、どの成功も承認ではありません（approval: false）。",
        "実 backend を含みません。どの object store につなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
        "保持期間・TTL（C-08）やアーカイブ（C-09）の削除・ライフサイクルの拡張は含みません。",
        "magic bytes で中身の種類を確かめられるのは、application/pdf・image/png・image/jpeg・text/plain・application/json だけです。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "object store の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/object-store/v1 でつなぎます。createObjectStoreClient({ cfg, port }) の put(ObjectPutRequest, bytes)・get(ObjectGetRequest) は、STORED・GET_VERIFIED・GET_REJECTED・REFUSED・UNKNOWN・HALTED などを返します（どれにも approval: false）。利用側が用意するもの: object backend への port（put・get、任意で lookup）。副作用は WORM が EXTERNAL_IRREVERSIBLE、OVERWRITE が EXTERNAL_REVERSIBLE、get は NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-02",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "保管完全性検証 — 保管完全性継手（Tsugite）",
      "summary": "保管完全性継手（Tsugite）は、保管先から読み戻した物が化けていないかを、書いたときの記録（recorded）と読み戻したときの観測（observed）の突き合わせで確かめる継手（つぎて）です。ハッシュ・大きさ・ハッシュアルゴリズムのどれか一つでも合わなければ、信頼しません（fail-closed）。ハッシュの計算と保管先への読み書きは、呼び出し側が行います。",
      "problem": "保管した物が、気づかないうちに化けてしまうこと（沈黙のビット腐敗）や、改ざん・すり替えを見逃して、そのまま使ってしまうことを防ぎます。大きさが一致しただけでは内容の一致とはみなさず、ハッシュの一致を別に求めます。",
      "use_when": [
        "保管先から読み戻した物が、書いたときと同じかを確かめたいとき（checksum や ETag で化けていないか）。",
        "ネットワークの中継（proxy）を置かずに、呼び出し側のライブラリとして完全性を確かめたいとき。",
        "採択したハッシュアルゴリズムだけを受け付け、それ以外のアルゴリズムの結果を信頼したくないとき。",
        "ハッシュと大きさを、自分の側ですでに計算・取得していて、その突き合わせだけを任せたいとき。"
      ],
      "do_not_use_when": [
        "ハッシュの計算や、保管先への書き込み・読み戻しまでこの継手にさせたいとき。これらは含まれておらず、呼び出し側が行って結果を渡します。",
        "記録（recorded）を残していない物を確かめたいとき。書いたときの hash・size・algo が無ければ、突き合わせられず、信頼しません。",
        "特定の保管 API の ETag の形式に、今すぐ合わせて使いたいとき。実際の保管 API と ETag の形式は、配備するときに config で決めます。",
        "SHA-256 以外のハッシュで確かめたいとき。確かめた範囲は SHA-256 の内容ハッシュです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "ハッシュを突き合わせます。記録と観測のハッシュが合わなければ（ビット腐敗・改ざん）、信頼しません（ok: false）。",
        "大きさを突き合わせます。記録と観測の大きさが合わなければ、信頼しません。",
        "大きさが同じでも、内容のハッシュが違えば信頼しません（大きさだけの一致では通しません）。",
        "ハッシュアルゴリズムを検めます。記録と観測でアルゴリズムが違うもの、採択していないアルゴリズムは信頼しません。",
        "欠落を検めます。ハッシュや大きさが欠けていれば、信頼しません。"
      ],
      "what_it_does_not_do": [
        "ハッシュを計算しません。保管先への書き込みも読み戻しもしません。呼び出し側が行い、その結果（recorded と observed）を渡します。",
        "backend につなぐ port を持ちません。この継手は、渡された値を突き合わせる純粋な関数だけです。",
        "require_size を false にすると、大きさの一致を求めなくなります（既定は true）。",
        "実際の保管 API と ETag の形式は決めていません。配備するときに config で決めます。",
        "完全性の事実を核としてまとめ、署名の対象や来歴に継ぐのは核（integrity-facts・provenance-facts）の側です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "保管 API の使い方の雛形ではありません。"
      ],
      "integration": "入口は src/guards.js の verifyIntegrity(cfg, recorded, observed)（純粋な関数）です。recorded に書いたときに記録した { hash, size, algo }、observed に読み戻したときに観測した { hash, size, algo } を渡すと、一致すれば { ok: true, errors: [] }、合わなければ { ok: false, errors: [...] } を返します。cfg は allowed_algos（採択したアルゴリズム）と require_size（既定 true）。port は無く、ハッシュの計算と保管先への読み書きは利用側が用意します。実行環境は Node.js（依存パッケージなし）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-03",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "トランザクション書込 — トランザクション書込継手（Tsugite）",
      "summary": "トランザクション書込継手（Tsugite）は、pin した backend・許可した論理の target・操作ごとの本体の digest を検めてから、全部か無しか（ALL_OR_NOTHING）の transaction を DB へ送り、DB が返す受領（TransactionalWriteReceipt）を照合する継手（つぎて）です。部分コミットは異常として停止し、ABORTED は外への効果が無いと照合できたときだけ受け取ります（fail-closed）。",
      "problem": "DB に「保存されたはず」と思い込んでしまうこと、つまり半分だけ書けた transaction を成功として扱ってしまうことを防ぎます。COMMITTED はすべての操作が入ったと照合できたとき、ABORTED は何も入っていないと照合できたときだけ受け取ります。",
      "use_when": [
        "複数の書き込みを、一貫性を保ったまま DB に書きたいとき（すべて入るか、何も入らないか）。",
        "DB が返す受領をそのまま信じず、要求（request_id・request_digest）と target の一致を照合してから、書けたと扱いたいとき。",
        "書き込み先を、pin した backend と許可した論理の target に限りたいとき。ファイルシステムの路を target にさせたくないとき。",
        "同じ要求を二度コミットしたくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
        "部分コミットや偽の受領が出たら、人が確かめるまで書き込みを止めたいとき。"
      ],
      "do_not_use_when": [
        "同じ契約 storage-persistence/v1 に属する C-05〜C-09 の機能が要るとき。この継手には含めていません。",
        "書き込みの成功を、承認や決裁として扱いたいとき。書けたことは承認ではありません。",
        "すぐに使える DB がほしいとき。実 DB は含まず、どの backend につなぐかはまだ決まっていません。",
        "ファイルシステムの路（先頭の / や \\、..）に書きたいとき。target にしません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "送り先を検めます。pin していない backend、許可していない target、ファイルシステムの路の target への要求は送りません。",
        "操作と本体を検めます。本体の改ざん（digest の不一致）、本体の過不足、同じ操作の重複、操作の数や本体の大きさが上限を超えるものは送りません。",
        "部分コミットを受理しません。COMMITTED は全件、ABORTED は 0 件で外への効果が無いことを求め、外れれば受理せず、以後は停止します（HALTED）。",
        "side effect を検めます。受領の side effect は要求の宣言から減らすことだけを認め、増やした受領は受理しません。COMMITTED は意図した side effect と一致する必要があります。",
        "受領（TransactionalWriteReceipt）を照合します。request_digest を偽った受領、別の target の受領は受理せず、以後は停止します（HALTED）。",
        "DB の障害は UNKNOWN とし、書けたとして扱いません。同じ request_id は二度コミットせず、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "一貫性を決めるのは核（reconcile-facts・policy-facts）の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "書けたことは承認ではありません（approval: false）。",
        "実 DB を含みません。どの backend につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "同じ契約 storage-persistence/v1 の C-05〜C-09 は含みません。優先順位の案では、継手を作らない（decision-only）か後回し（DEFER）です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "DB クライアントの雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createTxClient({ cfg, port }) の write(TransactionalWriteRequest, payloads)（payloads は { op_ref: bytes }）は、COMMITTED・ABORTED・REFUSED・UNKNOWN・HALTED などを返します（どれにも approval: false）。利用側が用意するもの: DB への port（commit と、任意で lookup）。副作用は要求で宣言する EXTERNAL_REVERSIBLE か EXTERNAL_IRREVERSIBLE（ABORTED は NONE か LOCAL_REVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-04",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "追記専用台帳 — 監査台帳継手（Tsugite）",
      "summary": "監査台帳継手（Tsugite）は、改変不能なイベント履歴を追記専用（append-only）の backend に積むときに、pin した台帳・通番（sequence）・二重追記の有無を検めてから送り、返る受領（AuditAppendReceipt）を照合できたときだけ台帳の先頭（head）を進める継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "外部の追記専用の保管先にイベントを積み重ねるとき、手元の台帳の先頭と保管先の実際がずれること（通番の巻き戻し・飛び、同じイベントの二重の追記、障害で結果が分からない書き込み）を防ぎます。受領を照合できない追記では台帳の先頭を進めないので、台帳の続き方を信頼できる状態に保てます。",
      "use_when": [
        "改変不能なイベント履歴を、外部の追記専用 backend に 1 件ずつ積み重ねたいとき。",
        "決まった 1 つの台帳（ledger_id・ledger_profile_revision・contract_revision で pin したもの）にだけ追記したいとき。",
        "通番が巻き戻ったり飛んだりしない、一本の続き方を保ちたいとき。",
        "同じイベントを二重に追記したくないとき。二重かどうかは event_id と封筒の digest で見分け、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
        "台帳の先頭・未解決の送信・停止の有無を、手元で確かめながら追記を進めたいとき（state() が { head, pending, halted } を返します）。"
      ],
      "do_not_use_when": [
        "書き換えや削除ができる保管先として使いたいとき。この継手は追記だけを扱います。",
        "イベントの本文そのものを台帳に積みたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
        "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
        "追記の成功を、承認や決裁として扱いたいとき。追記の成功は承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin した台帳を検めます。pin していない台帳への追記は送りません。",
        "通番（sequence）を検めます。台帳の先頭から見て通番が巻き戻っている・飛んでいるときは、送らず、受理しません。",
        "二重の追記を止めます。同じ event_id・同じ内容がもう追記済みなら backend に送らず ALREADY_APPENDED を返し、同じ event_id で内容が違うときは REFUSED を返します。",
        "未解決の送信があるあいだは、次の封筒を送らず REFUSED を返します。結果が分からなかった送信は照会（lookup）で解きます。",
        "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
        "受領（AuditAppendReceipt）の台帳・通番・event_id・対象が、送った封筒と食い違うときは受理せず、台帳の先頭を進めません。",
        "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
        "本文が混じった封筒、封筒の大きさの上限（max_envelope_bytes）を超えるイベント、種類を偽った封筒（content-type の偽り）を拒否します。"
      ],
      "what_it_does_not_do": [
        "台帳が改変できないかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
        "追記の成功は承認ではありません（approval: false）。",
        "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
        "透明性ログ（Merkle の包含証明）は扱いません。",
        "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
        "C-04 専用の部品ではありません。H-01（改変不能監査ログ）・H-08（改ざん検知チェーン）・H-02（アクセスログ）と同じ 1 つの部品が担い、試験と検査も共通です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する台帳と封筒の上限。state() は { head, pending, halted }。利用側は追記専用 backend への port（append、任意で lookup）を用意します。副作用は EXTERNAL_IRREVERSIBLE（取り消せない書き込み）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-05",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "冪等書込（WAL） — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend への冪等書込（IdempotentWrite）の要求を、pin した backend・許可した論理の target・書込の中身の上限で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。外への効果は高々 1 回で、一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "保管先への書き込みを再送したときに同じ内容を二重に書いてしまうこと、同じ冪等キーで別の中身を書いてしまうこと、そして backend の receipt を確かめないまま「書けた」と扱ってしまうことを防ぎます。",
      "use_when": [
        "再送があっても、保管 backend への書き込みの外への効果を高々 1 回にしたいとき。",
        "重複で効果なし（DEDUP_HIT）と返ったときも、保管されている中身が送った中身と同じかを確かめてから受け取りたいとき。",
        "書込の中身の digest を自分で確かめ、大きさの上限を超える中身を送りたくないとき。",
        "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
      ],
      "do_not_use_when": [
        "トランザクション書込が要るとき。C-03 のトランザクション書込は別の部品（transactional-write-tsugite）です。",
        "重複かどうかの決定をこの継手に任せたいとき。重複の検出は核（reconcile-facts）の役目です。",
        "ファイルシステム上の場所を書き先にしたいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "書けたことを、承認や決裁として扱いたいとき。書けたことは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "同じ冪等キーで別の中身を書く要求は送りません（REFUSED）。",
        "書込の中身を検めます。大きさの上限を超える中身、digest と合わない（改ざんされた）中身は送りません。",
        "重複（DEDUP_HIT）の receipt が新しい効果を伴っていれば受理しません。DEDUP_HIT は効果なし（NONE）でなければなりません。",
        "保管された中身（stored_digest）が送った中身と違えば、DEDUP_HIT でも受理しません。同じキーで別の中身は異常として扱います。",
        "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
        "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。"
      ],
      "what_it_does_not_do": [
        "重複かどうかを決めるのは核（reconcile-facts）の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "書けたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその冪等書込の面で、F-04（冪等キー処理）も同じ面で担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "保管サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request, bytes) に IdempotentWrite の要求と書込の中身 bytes（必須）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に dedup_result を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、backend・target・上限の設定。副作用は EXTERNAL_*（DEDUP_HIT は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-06",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "バックアップ・スナップショット — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend へのスナップショット（Snapshot）の要求を、pin した backend と許可した論理の target で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "スナップショットの要求を送るとき、許可していない対象を送ってしまうこと、そして backend の receipt が本当にその対象のものかを確かめないまま「取れた」と扱ってしまうことを防ぎます。この継手が照合できたスナップショットだけが、模擬の復元（C-07）の元になります。",
      "use_when": [
        "保管の backend に論理の target のスナップショットの要求を送り、receipt を照合できてから、取れたことを事実として受け取りたいとき。",
        "スナップショットの receipt を、その snapshot_ref の中身の digest（content_digest）と対にして照合したいとき。",
        "照合できたスナップショットだけを、模擬（SYNTHETIC）・dry-run の復元の元として扱いたいとき。",
        "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
      ],
      "do_not_use_when": [
        "スナップショットから実際に戻すこと（本番の PITR）まで求めるとき。実際に壊す復元は範囲外で、復元は模擬・dry-run だけです。",
        "ファイルシステム上の場所の複製を取りたいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "取れたことを、承認や決裁として扱いたいとき。取れたことは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
        "スナップショットの receipt の効果は、取り消せる外部への効果（EXTERNAL_REVERSIBLE）までです。それより効果を増やす receipt は受理しません。",
        "照合できたスナップショットを state() の snapshots に残し、照合していないスナップショットからの復元の要求は送りません。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
        "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
      ],
      "what_it_does_not_do": [
        "スナップショットが照合できたことは、そこから実際に戻せることを確かめたものではありません。実際に壊す復元（本番の PITR）は範囲外です。",
        "何をいつ取るかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "取れたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はそのスナップショットの面です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "保管サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Snapshot の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に snapshot_ref・snapshot_state を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する backend・許可する target の設定。副作用は取り消せる外部への効果（EXTERNAL_REVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-07",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "復旧・PITR（模擬・dry-run だけ） — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、復旧（Restore）の要求を模擬（SYNTHETIC）と dry-run に限って検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。実際に壊す復元（本番の PITR）はせず、その要求は送りません。元にできるのは、この継手が照合したスナップショットだけです（fail-closed）。",
      "problem": "復元の要求を送るとき、照合していないスナップショットから戻そうとすること、dry-run なのに「適用した」とする結果を信じてしまうこと、実際に壊す復元の要求が送られてしまうことを防ぎます。この継手が扱うのは模擬・dry-run の復元だけで、実際にある時点へ戻すことはしません。",
      "use_when": [
        "照合済みのスナップショットを元に、模擬（SYNTHETIC）または dry-run の復元の要求を送り、その receipt を照合したいとき。",
        "dry-run の復元の結果として、効果なし（NONE）の receipt だけを受け取りたいとき。",
        "復元の元を、この継手が CREATED と照合したスナップショットに限りたいとき。"
      ],
      "do_not_use_when": [
        "実際にある時点へ戻したいとき（本番の PITR）。実際に壊す復元は範囲外で、契約も模擬・dry-run だけを定めます。この継手はその要求を送りません。",
        "事故のときに実際に復旧する手段として使いたいとき。この継手は実際の復旧をしません。",
        "この継手が照合していないスナップショットから戻したいとき。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "実際に壊す復元のモードの要求は送りません。",
        "この継手が CREATED と照合していないスナップショットからの復元の要求は送りません。",
        "dry-run の要求に「適用した」とする receipt は受理しません。APPLIED_SYNTHETIC は模擬（SYNTHETIC）の要求のときだけ受け取ります。",
        "復元の receipt の効果は、効果なし（NONE）か手元で取り消せる効果（LOCAL_REVERSIBLE）までで、dry-run は NONE だけです。効果を増やす receipt は受理しません。",
        "request_digest の偽り、別の target の receipt は受理せず、以後停止します（HALTED）。",
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
        "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
      ],
      "what_it_does_not_do": [
        "実際の復旧はしません。実際に壊す復元（本番の PITR）は範囲外で、契約も模擬・dry-run だけを定めます。DONE に添える restore_state は、模擬・dry-run の復元の結果です。",
        "ファズは backend を叩かず、試験は保管 backend の代役を使っています。実際の backend である時点へ戻せることは確かめていません。",
        "何をいつ戻すかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "模擬の復元が照合できたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその模擬の復元の面です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Restore の要求（模擬か dry-run）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に restore_state を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）。副作用は NONE か LOCAL_REVERSIBLE（dry-run は NONE）で、実際の復旧は含みません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-08",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "保持・TTL — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、保持（Retention）の要求（EVALUATE・RETAIN・EXPIRE）を、pin した保持方針と論理の target で検めてから送り、receipt を契約どおりに照合する継手（つぎて）です。取り消せない削除（EXPIRE）は、設定 allow_expire（既定 false）で許し、リーガルホールドの無い対象にだけ送ります（fail-closed）。",
      "problem": "保持期限による削除を backend へ送るとき、許していない削除やリーガルホールド中の対象の削除を送ってしまうこと、期限切れの判定だけのはずが削除されたと扱うこと、backend の receipt を確かめないまま「消せた」と扱うことを防ぎます。",
      "use_when": [
        "保持方針に沿った削除（EXPIRE）の要求を、設定で許したときだけ backend へ送りたいとき。",
        "リーガルホールドの事実（核・人の決定）を受け取り、以後その対象の EXPIRE を送らないようにしたいとき。",
        "削除せずに、期限切れかどうかの判定（EVALUATE）だけを backend に求めたいとき。",
        "消せたこと（deleted: true）を、EXPIRE の receipt が EXPIRED と照合できたときだけ事実として扱いたいとき。"
      ],
      "do_not_use_when": [
        "保持期限やリーガルホールドをこの継手に決めさせたいとき。その決定は核と人の役目で、この継手は事実を受け取って守るだけです。",
        "期限が来たら自動で消す仕組みそのものがほしいとき。この継手は渡された保持の要求を検めて送るだけで、EXPIRE は設定 allow_expire が true でなければ送りません。",
        "期限で別の階層へ移したいとき。階層の移動は Archive の要求で、C-09 アーカイブ・階層化の面です。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定 allow_expire が true でないとき、EXPIRE を送りません。",
        "リーガルホールド中の対象には EXPIRE を送りません。",
        "EVALUATE の receipt が削除を伴っていれば受理しません。EVALUATE の EXPIRED は期限切れの判定だけで、削除ではありません。",
        "RETAIN に EXPIRED を返す receipt は、異常として受理しません。",
        "pin した一覧にない保持方針の要求は送りません。",
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。backend の障害は UNKNOWN とし、同じ request_id を二度送りません。"
      ],
      "what_it_does_not_do": [
        "保持期限の決定・リーガルホールドの決定はしません。核（redact-facts・policy-facts）と人の役目で、この継手はその事実を受け取って守るだけです。",
        "消せたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその保持の面で、H-06（保持強制）も同じ面で担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "保管サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Retention の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に decision・deleted・effect を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する保持方針・allow_expire の設定。副作用は EXPIRE が EXTERNAL_IRREVERSIBLE、ほかは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-09",
      "category": "C/保管・永続化",
      "kind": "Tsugite",
      "title": "アーカイブ・階層化 — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend への階層の移動（Archive）の要求を、pin した階層と許可した論理の target で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "データを別の階層へ移す要求を送るとき、pin していない階層や許可していない対象へ送ってしまうこと、そして backend の receipt を確かめないまま「移せた」と扱ってしまうことを防ぎます。",
      "use_when": [
        "古いデータを、pin した階層へ移す要求を backend へ送りたいとき。",
        "移した結果（transition_state・archive_ref）を、receipt を照合できてから事実として受け取りたいとき。",
        "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
      ],
      "do_not_use_when": [
        "どのデータをいつ移すかをこの継手に決めさせたいとき。決定は核の役目で、この継手は送る前と受け取った後の検めだけです。",
        "取り消せない削除を伴わせたいとき。アーカイブの効果は取り消せる外部への効果（EXTERNAL_REVERSIBLE）までで、取り消せない削除は保持（C-08）の EXPIRE の扱いです。",
        "ファイルシステム上の場所へ移したいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していない階層への移動の要求は送りません。",
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "アーカイブの receipt の効果は、取り消せる外部への効果（EXTERNAL_REVERSIBLE）までです。それより効果を増やす receipt は受理しません。",
        "request_digest の偽り、別の target の receipt は受理せず、以後停止します（HALTED）。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
        "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
      ],
      "what_it_does_not_do": [
        "何をいつ移すかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "移せたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその階層の移動の面です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "保管サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Archive の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に transition_state・archive_ref を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する backend・階層（tiers）・許可する target の設定。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-10",
      "category": "C/保管・永続化",
      "kind": "Component",
      "title": "生データの保管（追記だけ） — raw の保管（追記だけ・内容アドレス・区画分け）の部品（Component）",
      "summary": "raw の保管の部品（Component）は、取得した raw を型にはめずに、retention_class（pii・business・evidence）ごとに別の保管先へ追記だけで置く部品です。取得の記録の部品(Primitive)（N-09）で fetch.json を作り、渡した bytes が記録の name・byte_size・sha256 と合うことを確かめ、payload を記録の順に、最後に fetch.json を冪等書込の継手（C-05）で書きます。冪等キーはパスです。read は C-01 の継手で期待する sha256 を添えて読み、digest が合ったときだけ bytes を返します。削除・上書きの操作はありません。",
      "problem": "「保存してある」の裏で、元のまま残っているかは誰も確かめません。この部品は、記録と合わない bytes を置くこと、一度置いたキーを別の中身で上書きすること、PII をほかの区画と混ぜること、読んだ bytes が置いたものと違うのに使ってしまうことを防ぎます。",
      "use_when": [
        "取得したファイル（1〜10000 個）と fetch.json を、raw/<source>/<folder_stamp>/payload/<name> と raw/<source>/<folder_stamp>/fetch.json のキーで、追記だけで 1 回だけ置きたいとき（store）。",
        "保持区分ごとに保管先を分け、PII の区画には、ほかの区画と別の backend・別のクライアントを当てたいとき。どの書込も、その取得の retention_class の区画の target にだけ送ります。",
        "PARTIAL の取得も、取れた分をほかと同じに残したいとき。続きは次の取得で、別の fetched_at の別のフォルダーになります。",
        "置いた raw を、期待する sha256 を添えて読み、継手の検めの後に自分でも sha256 を計算し直して、合ったときだけ bytes の写しを受け取りたいとき（read）。",
        "結果の分からなかった書込を、同じ要求で送り直して残りの書込を続けたいとき（resolvePending）。同じ取得をもう一度 store すれば、書けた分は DEDUP_HIT で続きから書けます。"
      ],
      "do_not_use_when": [
        "保持の期間を数える・期限で消す・PII の期間の決まりを守らせることまで任せたいとき。この部品は区画を分けて置くだけです。保持と削除は backend の方針（C-08 など）の側です。",
        "取得した中身がプラットフォームの中身と同じかを確かめたいとき。確かめるのは、渡した bytes が N-09 の記録の name・byte_size・sha256 と合うことだけです。",
        "PII の区画が本当に別のストレージであることを確かめたいとき。確かめるのは backend_profile_ref の文字列とクライアントのオブジェクトが違うことまでで、2 つの名前が同じ物を指していても見分けられません。",
        "ファイルの大きさの上限をこの部品に任せたいとき。上限は C-05 の継手の max_payload_bytes で決めます。大きなファイルは送るたびに写しを作ります。",
        "ほかのプロセスや再起動の前に書いたキーまで、この部品に RAW_CONFLICT で見分けさせたいとき。控えはプロセスの中だけで、そのキーは継手と backend が断ります（どちらでも上書きはしません）。",
        "同じ request_prefix の 2 つのインスタンスで、同じクライアントを使いたいとき。継手が同じ request_id を ALREADY_DONE で答え、別の要求が前の答えで返ります。request_prefix はクライアントごとに一意にします。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・区画が 3 つちょうどでない・2 つの区画が同じ保管先・pii がほかの区画と同じ backend かクライアント・port の関数が無いとき、INVALID_OPTIONS で止めます。",
        "操作の入力の形が違うとき（欄の過不足、files が 1〜10000 個の素の配列でない、name の重なり、bytes が Uint8Array でない、read の key が raw/ で始まらない など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "N-09 が記録を作らないとき FETCH_RECORD_REFUSED（N-09 の errors を添える）、N-09 が例外を投げたとき FETCH_RECORD_PORT_FAILED、答えが食い違うとき（canonical の sha256・正準形・schema・入力の欄・folder_stamp・files の形）FETCH_RECORD_INVALID で、何も送りません。",
        "渡した files が記録の files と合わないとき、FILE_MISMATCH で何も送りません。errors[0] に name と reason（MISSING・SIZE・DIGEST・EXTRA）を添えます。",
        "書込の計画のキーに、この部品が前に別の sha256 を送っているとき、RAW_CONFLICT で何も送りません（追記だけ・上書きしない）。",
        "書込の結果が分からないとき STORE_UNKNOWN で同じ要求を保ち、保留の間の store を STORE_PENDING で止めます。継手が送らなかったとき WRITE_REFUSED、停止・receipt の照合の失敗・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED で、次へ進みません。",
        "read で、保管物が無いとき OBJECT_NOT_FOUND、digest が合わないとき（継手の GET_REJECTED か、自分で計算し直した sha256 の違い）DIGEST_MISMATCH、結果が分からないとき READ_UNKNOWN、送らなかったとき READ_REFUSED、停止・形の違いのとき READ_FAILED で、bytes を返しません。",
        "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さない・投げたとき CLOCK_FAILED で何も送らず、前の操作が終わっていないとき BUSY で止めます。"
      ],
      "what_it_does_not_do": [
        "raw の保管の部品は継手ではありません。自分の Request/Receipt は無く、書込と読出しという外への効果は、束ねた C-05・C-01 の継手がそれぞれの契約で起こします。この部品自身は外へ触れません。",
        "束ねた部品の意味（記録できた ≠ 取得の正しさ・書けた ≠ 受け入れ・読めた ≠ 中身の正しさ）は、それぞれの部品の側に残ります。",
        "削除・上書きの操作はありません。送るのは IdempotentWriteRequest と ObjectGetRequest だけです。保持の期間は数えず、消しません。",
        "書けたこと・読めたことは、中身の正しさでも受け入れでも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "C-05 のキーと C-01 の object_ref が同じ保管物を指すか（書いた raw を read で読めるか）は backend profile の役目です。試験では同じ保管物を指す代役で確かめました。",
        "本物の N-09・C-05・C-01 には、この部品の試験では接続していません。単体試験とファズは代役で、本物との結線は別の結線の検めで確かめます。状態（送ったキーの控え・保留）はプロセスの中だけで、再起動で消えます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のデータレイク・オブジェクトストレージの雛形ではありません。"
      ],
      "integration": "src/raw-store.js の createRawStore(options) で作ります。受け取るのは partitions { pii, business, evidence }・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。操作は store・read・resolvePending・status で、どの結果にも approval: false が付きます。利用側が用意する port は fetchRecord（N-09）、区画ごとの write.<class>.submit（C-05 の継手のクライアント）と read.<class>.get（C-01 の継手のクライアント）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "C-11",
      "category": "C/保管・永続化",
      "kind": "Component",
      "title": "区分ごとの世代バックアップ — 区画ごとの世代バックアップと復元の検めの部品（Component）",
      "summary": "区画ごとの世代バックアップと復元の検めの部品（Component）は、保持区分（pii・business・evidence）ごとに、別の保管先・別のクライアント・別の世代の数え方でバックアップを取る部品です。backup はスナップショットの継手（C-06）で作らせ、照合できたものだけを世代の記録として冪等書込の継手（C-05）で残します。verifyRestore は模擬の復元（C-07、DRY_RUN か SYNTHETIC）だけを送り、復元した中身の digest が世代の content_digest と C-02 の継手で一致したときだけ「復元できると確かめた」にします。",
      "problem": "バックアップは戻すときまで試されません。この部品は、照合できないスナップショットを世代にすること、PII をほかの区画と混ぜること、世代を上書きすること、確かめていないバックアップを「復元できる」と扱うことを防ぎます。",
      "use_when": [
        "区画ごとにスナップショットを作らせ、照合できたものを次の世代として backup/<class>/<seq>.json に 1 回だけ記録したいとき（backup）。世代の番号は last_seq + 1 から、書けた世代だけで数えます。",
        "記録した世代から SYNTHETIC の模擬の復元を送り、readRestored が返す復元した中身の digest を、世代の content_digest と照合したいとき（verifyRestore）。",
        "世代が keep_generations を超えたことを、over_limit の事実として受け取りたいとき。古い世代は消しません。",
        "区画ごとの世代と確かめの結果（restore_verified）・保留を一覧したいとき（status）。",
        "結果の分からなかった送信を、その区画だけ保留にし、同じ要求で送り直したいとき（resolvePending）。保留の間も、ほかの区画は止まりません。"
      ],
      "do_not_use_when": [
        "本番への復元（実際に壊す復元）をしたいとき。送るのは DRY_RUN か SYNTHETIC だけで、本番で復元できるかは確かめていません。",
        "古い世代を期限で消したいとき。keep_generations は over_limit の事実を返すだけで、消すのは backend の保持（C-08 など）の役目です。",
        "取ったばかりのバックアップを「復元できる」と扱いたいとき。verifyRestore の digest の照合（SYNTHETIC・readRestored あり）が済むまで restore_verified: false です。readRestored が無い・DRY_RUN のときは digest_verified: null です。",
        "スナップショットの content_digest が中身を正しく表しているか、readRestored が本当に復元した中身から digest を計算したかまで確かめたいとき。この部品は返った値どうしを照合するだけです。",
        "再起動の前に取った世代を、そのまま verifyRestore したいとき。状態はプロセスの中だけで、再起動後は last_seq を渡して数え直し、前のプロセスの世代は verifyRestore の対象になりません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・区画が 3 つちょうどでない・2 つの区画が同じ保管先・keep_generations や last_seq の形・port の関数が無いとき INVALID_OPTIONS、操作の入力の形が違うとき INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "スナップショットの結果が分からないとき SNAPSHOT_UNKNOWN（同じ要求を保つ）、送らなかったとき SNAPSHOT_REFUSED、CREATED でない・停止・結果や content_digest の形が違うとき SNAPSHOT_FAILED で、世代にしません。",
        "世代の記録の書込の結果が分からないとき RECORD_UNKNOWN（同じ要求を保つ）、送らなかったとき（同じキーの別の中身を含む）RECORD_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき RECORD_FAILED で、世代として数えません。",
        "verifyRestore で、この部品が記録した世代として無い snapshot_ref のとき UNKNOWN_GENERATION で止めます。",
        "復元の結果が分からないとき RESTORE_UNKNOWN、継手が送らなかった・backend が復元を断ったとき RESTORE_REFUSED、停止・知らない restore_state・DRY_RUN なのに適用したと言ったとき RESTORE_FAILED で止めます。",
        "復元した digest が世代の content_digest と合わないとき（C-02 が ok: true でない、sha256 でない、値が違う）RESTORE_DIGEST_MISMATCH（digest_verified: false）、readRestored が投げた・形が違うとき RESTORED_READ_FAILED、C-02 の関数が投げたとき INTEGRITY_PORT_FAILED（どちらも digest_verified: null）で、確かめにしません。",
        "その区画に結果の分からない送信があるとき CLASS_PENDING、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING、timestamp が実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます。"
      ],
      "what_it_does_not_do": [
        "区画ごとの世代バックアップと復元の検めの部品は継手ではありません。自分の Request/Receipt は無く、スナップショット・模擬の復元・世代の記録の書込という外への効果は、束ねた継手（C-05・C-06・C-07）がそれぞれの契約で起こします。この部品自身は外へ触れません。",
        "束ねた部品の意味（作れた ≠ 中身の正しさ・模擬の復元 ≠ 本番の復元・照合できた ≠ 承認）は、それぞれの部品の側に残ります。",
        "削除・保持・アーカイブの要求は送らず、古い世代を消しません。世代は上書きしません。",
        "結果は承認ではありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "世代の記録が保存されたのに結果が崩れたときは、その seq のキーが backend に残り、次の backup の記録は断られ続けます（fail-closed）。直すのは呼び出し側で、last_seq を合わせます。",
        "本物の storage-lifecycle・storage-integrity の継手と backend には、この部品の試験では接続していません。単体試験とファズは代役で、本物との結線は別の結線の検めで確かめます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のバックアップ製品の雛形ではありません。"
      ],
      "integration": "src/backup.js の createBackup(options) で作り、{ ok, errors, instance } を返します。options は policies { pii, business, evidence }（target・keep_generations・任意の last_seq）・request_prefix・ports・任意の timestamp です。操作は backup・verifyRestore・resolvePending・status で、結果に approval: false が付きます。port は区画ごとの storage.<class>.submit（storage-lifecycle の継手のクライアント）・verifyIntegrity（C-02）・任意の readRestored です。資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-01",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "メール配信（transactional） — メール配信継手（Tsugite）",
      "summary": "メール配信継手（Tsugite）は、確認・領収・リマインドなどのメールを、pin した宛先・検証済みの同意・最小化の記録・本文の digest・宛先偽装の無いヘッダー・冪等キーを検めてからメール backend へ送り、backend が返す受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。一つでも合わなければ送りません（fail-closed）。",
      "problem": "メールを「届いた」で終わらせてしまうこと、つまり宛先を偽ったメールや同意の無いメール、化けたメールを送ってしまうこと、そして送れたことを届いたことと取り違えることを防ぎます。送れた状態（REQUESTED）は到達ではなく、delivered: false として扱います。",
      "use_when": [
        "確認・領収・リマインドなどのメールを、外部のメール backend から送りたいとき。",
        "送り先を、pin した宛先の表（recipient_ref とメールアドレス）に限りたいとき。",
        "検証済みの同意が無い宛先や、bounce・苦情を記録した宛先には送りたくないとき。",
        "同じメールを二重に送りたくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
        "偽の受領が出たら、人が確かめるまで送信を止めたいとき。"
      ],
      "do_not_use_when": [
        "SMS・push・chat で送りたいとき。この継手は email だけで、ほかの channel は範囲外です。",
        "到達の確認（D-05）や、bounce・苦情の判定（D-06）をこの継手にさせたいとき。これらは核の側で、この継手はその結果を受け取るだけです。",
        "Cc・Bcc で複数の相手に送りたいとき。To はちょうど 1 つで pin した宛先と一致する必要があり、Cc・Bcc・Resent-* は送りません。",
        "送信の成功を、到達や承認として扱いたいとき。送れたことは到達ではなく、到達は承認ではありません。同意も Authority ではありません。",
        "すぐに使えるメール backend がほしいとき。実メール backend（SMTP・各社の API）は含まず、どれにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "宛先の偽装を検めます。To が pin した宛先と合わない、To が 2 つある、Cc・Bcc がある、ヘッダーに CR/LF の注入があるメールは送りません。pin していない宛先にも送りません。",
        "同意を検めます。同意の無い送信、検証されていない同意での送信は送りません。",
        "bounce・苦情を記録した宛先には送りません。",
        "本文を検めます。digest と合わない本文（改ざん）や、上限を超える大きさの本文は送りません。",
        "二重送信を検めます。同じ idempotency_key はもう一度送りません（ALREADY_SENT）。",
        "受領（NotificationReceipt）を照合します。request_digest を偽った受領、証拠の無い delivered、UNKNOWN を送信に格上げした受領、side effect の食い違いは受理せず、以後は停止します（HALTED）。",
        "backend の障害は UNKNOWN とし、送信として扱いません。再送は照会（lookup）で解きます。",
        "秘密の渡し方を検めます。SecretRef の隣に生の秘密を置いた要求は拒否します。"
      ],
      "what_it_does_not_do": [
        "宛先の許可・配信の同意・本文の最小化を決めるのは核（policy-facts・consent-facts・redact-facts）の役目です。この継手は、その結果を受け取って検めるだけです。",
        "送れたことは到達ではありません（REQUESTED は delivered: false）。どの結果も承認ではありません（approval: false）。",
        "実メール backend（SMTP・各社の API）を含みません。port の実装と pin する対象は未定で、接続するときに別の関門で決めます。",
        "email 以外の channel（SMS・push・chat）は扱いません。",
        "到達の確認（D-05）と、bounce・苦情の判定（D-06）はしません。その結果を受け取るだけです。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "メール API の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createMailClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。bounce・苦情は recordSuppression で渡します。利用側が用意するもの: メール backend への port（send と、任意で lookup）。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-02",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "SMS 配信 — SMS・push 配信継手（Tsugite）",
      "summary": "SMS・push 配信継手（Tsugite）は、SMS の通知要求（NotificationRequest、channel sms）を、pin した宛先表・検証済みの同意・最小化の記録・冪等キー・本文の digest と上限・SMS の本文の形で検めてから送信基盤へ渡し、戻った受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。SMS を運ぶ継手で、本人確認の機能は含みません。一つでも合わなければ送りません（fail-closed）。",
      "problem": "SMS を送るとき、pin していない番号や形の崩れた番号（E.164 でない番号）へ送ってしまうこと、同意や最小化の記録が無いまま送ること、同じ通知を二度送ること、そして「依頼した」を「届いた」「読まれた」と取り違えることを防ぎます。",
      "use_when": [
        "通知の SMS を、pin した宛先表の番号（E.164）にだけ送りたいとき。",
        "送る前に、検証済みの同意と本文の最小化の記録があることを確かめたいとき（どちらも設定で必須にできます）。",
        "同じ通知を二度送りたくないとき。冪等キーを必須にし、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
        "不達・苦情のあった宛先に、以後は送りたくないとき（recordSuppression）。",
        "依頼した・届いた・読まれた・確認されたを、それぞれの証拠で分けて扱いたいとき。"
      ],
      "do_not_use_when": [
        "本人確認に使いたいとき。この継手は SMS の通知要求を運ぶ継手で、本人確認の機能は含みません。SMS が届いた・読まれたことは、本人を確かめたことではありません。",
        "メール・Webhook の channel で送りたいとき。channel は sms・push だけです。D-01 は mail 継手、D-04 は notify 継手です。",
        "送れたこと・届いたことを、承認や決裁として扱いたいとき。送れた・届いたは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える送信基盤がほしいとき。実の送信基盤（どの SMS のサービスか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "宛先を検めます。pin した宛先表に無い宛先、止めた宛先（不達・苦情）、要求の channel と宛先の channel が合わないもの、email・webhook の channel の要求は送りません。",
        "宛先表の形を検めます。SMS の宛先は E.164（+ と国番号から 7〜15 桁）で、表に 1 件でも形の崩れた宛先（E.164 でない・空白を含む）があれば、表全体を使いません。",
        "SMS の本文を検めます。UTF-8 として正しくない本文、改行のほかの制御文字を含む本文は送りません。",
        "本文の digest の改ざんと、上限（max_payload_bytes）を超える本文を送りません。",
        "同意と最小化を検めます。設定で必須にしたとき、同意が無い・検証されていない要求、最小化の記録が無い要求は送りません。",
        "冪等キーの無い要求は送りません。同じ冪等キーは二度送りません（ALREADY_SENT）。",
        "送信基盤の認証は SecretRef の場所だけを運びます。SecretRef の欄に秘密そのものが入った要求は送りません。",
        "受領（NotificationReceipt）を照合します。証拠の無い到達・既読・確認の状態、message_id の無い送信、side effect の食い違い、request_digest の偽りは受理せず、そのあとは停止します（HALTED）。送信基盤の障害は UNKNOWN とし、送信として扱いません。"
      ],
      "what_it_does_not_do": [
        "本人確認はしません。SMS の通知要求を運ぶ継手で、本人確認の機能は含みません。届いた・読まれたは、本人の確認ではありません。",
        "送れたことは到達ではありません。SENT の delivered は、到達の証拠がある状態のときだけ true です。依頼しただけ（REQUESTED）は届いたではありません。",
        "SMS の文字数（分割・文字コードによる上限）は見ません。上限は bytes だけです。",
        "同意が有効か・最小化が十分かを決めるのは核（consent-facts・redact-facts）の役目で、宛先の許可は policy-facts の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の送信基盤を含みません。どのサービスにつなぐか、port の実装と宛先表は未定で、接続するときに別の関門で決めます。",
        "送れた・届いたは承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（channel sms）でつなぎます。createSmsPushClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 送信基盤への port（send と、任意で lookup）と、cfg の宛先表 recipients。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-03",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "プッシュ通知 — SMS・push 配信継手（Tsugite）",
      "summary": "SMS・push 配信継手（Tsugite）は、push の通知要求（NotificationRequest、channel push）を、pin した宛先表（端末の参照）・検証済みの同意・最小化の記録・冪等キー・本文の digest と上限・push の本文の形で検めてから送信基盤へ渡し、戻った受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。push の本文で送り先を差し替えさせず、一つでも合わなければ送りません（fail-closed）。",
      "problem": "push 通知を送るとき、本文に送り先（token・to・topic など）を入れて pin していない端末へ差し替えてしまうこと、同意や最小化の記録が無いまま送ること、同じ通知を二度送ること、そして「依頼した」を「届いた」「読まれた」と取り違えることを防ぎます。",
      "use_when": [
        "push 通知を、pin した宛先表の端末の参照（空白の無い参照）にだけ送りたいとき。",
        "送り先は宛先表だけで決め、本文（JSON）から送り先を変えさせたくないとき。",
        "送る前に、検証済みの同意と本文の最小化の記録があることを確かめたいとき（どちらも設定で必須にできます）。",
        "同じ通知を二度送りたくないとき。冪等キーを必須にし、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
        "不達・苦情のあった宛先に、以後は送りたくないとき（recordSuppression）。",
        "依頼した・届いた・読まれた・確認されたを、それぞれの証拠で分けて扱いたいとき。"
      ],
      "do_not_use_when": [
        "push の本文の中身の意味（通知として正しいか）を確かめたいとき。この継手が止めるのは送り先の差し替えだけです。",
        "メール・Webhook の channel で送りたいとき。channel は sms・push だけです。D-01 は mail 継手、D-04 は notify 継手です。",
        "送れたこと・届いたことを、承認や決裁として扱いたいとき。送れた・届いたは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える送信基盤がほしいとき。実の送信基盤（どの push のサービスか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "宛先を検めます。pin した宛先表に無い宛先、止めた宛先（不達・苦情）、要求の channel と宛先の channel が合わないもの、email・webhook の channel の要求は送りません。",
        "宛先表の形を検めます。push の宛先は空白の無い端末の参照で、表に 1 件でも形の崩れた宛先があれば、表全体を使いません。",
        "push の本文を検めます。UTF-8 の JSON オブジェクトでない本文と、token・to・topic・registration_ids・condition・target を持つ本文（送り先の差し替え）は送りません。",
        "本文の digest の改ざんと、上限（max_payload_bytes）を超える本文を送りません。",
        "同意と最小化を検めます。設定で必須にしたとき、同意が無い・検証されていない要求、最小化の記録が無い要求は送りません。",
        "冪等キーの無い要求は送りません。同じ冪等キーは二度送りません（ALREADY_SENT）。",
        "送信基盤の認証は SecretRef の場所だけを運びます。SecretRef の欄に秘密そのものが入った要求は送りません。",
        "受領（NotificationReceipt）を照合します。証拠の無い到達・既読・確認の状態、message_id の無い送信、side effect の食い違い、request_digest の偽りは受理せず、そのあとは停止します（HALTED）。送信基盤の障害は UNKNOWN とし、送信として扱いません。"
      ],
      "what_it_does_not_do": [
        "送れたことは到達ではありません。SENT の delivered は、到達の証拠がある状態のときだけ true です。依頼しただけ（REQUESTED）は届いたではありません。",
        "push の本文の中身の意味（通知として正しいか）は見ません。止めるのは送り先の差し替えだけです。",
        "同意が有効か・最小化が十分かを決めるのは核（consent-facts・redact-facts）の役目で、宛先の許可は policy-facts の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の送信基盤を含みません。どのサービスにつなぐか、port の実装と宛先表は未定で、接続するときに別の関門で決めます。",
        "送れた・届いたは承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の SMS・push サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（channel push）でつなぎます。createSmsPushClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 送信基盤への port（send と、任意で lookup）と、cfg の宛先表 recipients。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-04",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "Webhook 配信 — 通知配信継手（Tsugite）",
      "summary": "通知配信継手（Tsugite）は、外部へ Webhook などのイベント通知を送る前に、その通知を送ってよいかだけを判定する継手（つぎて）です。pin した宛先・許可したイベント・冪等キー・署名済みの payload・重複の無さを検め、一つでも欠ければ「送らない」と判定します（fail-closed）。送信・再送・payload の署名はしません。送る前の判定だけです。",
      "problem": "連携の裏配管として外部へ通知を送るとき、pin していない宛先（任意の URL）へ送ってしまうこと（SSRF）、許可していないイベントを送ってしまうこと、同じ通知を二重に送ってしまうこと、署名の無い payload を送ってしまうことを、送る前の判定で防ぎます。",
      "use_when": [
        "自分の送信処理の前に置いて、Webhook などの通知を送ってよいかを判定させたいとき。",
        "送り先を pin した宛先の集合に限り、任意の URL への送出を拒否したいとき（anti-SSRF）。",
        "送ってよいイベントの種別を、許可した一覧（allowlist）に限りたいとき。",
        "もう送った冪等キーの集合を自分で持っていて、同じ通知の二重送信を送る前に止めたいとき。"
      ],
      "do_not_use_when": [
        "Webhook を実際に送る部品、失敗したときに再送する部品、payload に署名する部品がほしいとき。この継手はどれもしません。送る前の判定だけです。",
        "送った通知が届いたかを、受領（receipt）で確かめたいとき。到達の receipt の検めは含みません。",
        "payload の署名そのものを検めたいとき。署名済みかどうかは、呼び出し側が渡す真偽値（payload_signed）で判定します。",
        "冪等キーや署名の要求を外したまま使うとき。require_idempotency_key・require_signed_payload を false にすると、その検めが外れます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "宛先を検めます。pin した宛先の集合の外への通知は、送らないと判定します（任意の URL への送出を拒否。SSRF）。",
        "イベントを検めます。許可していないイベント種別の通知は、送らないと判定します。",
        "冪等キーを検めます。冪等キーの無い通知は、送らないと判定します（require_idempotency_key が既定の true のとき）。",
        "署名を検めます。payload_signed が真でない（未署名の）通知は、送らないと判定します（require_signed_payload が既定の true のとき）。",
        "重複を検めます。渡された「もう送った冪等キーの集合」にあるキーの通知は、二重送信として送らないと判定します。"
      ],
      "what_it_does_not_do": [
        "Webhook を送信しません。失敗したときの再送もしません。payload に署名もしません。これらは利用側が行います。",
        "到達の受領（receipt）を検めません。この継手がするのは送る前の判定だけです。",
        "冪等キーと署名の要求は設定で外せます。require_idempotency_key・require_signed_payload を false にすると、その検めが外れ、冪等キーの欠落・未署名を理由には止めなくなります（どちらも既定は true）。",
        "もう送った冪等キーの集合（delivered）を保持しません。呼び出し側が渡します。",
        "実際の配信 API と署名の方式は決めていません。配備するときに config で決めます。",
        "配信の同意や承認の経路、配信の来歴は核（consent-facts・provenance-facts）の側です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "通知 API の使い方の雛形ではありません。"
      ],
      "integration": "入口は src/guards.js の evaluate(cfg, request, delivered)（純粋な関数。送る前の判定）です。request に { recipient, event_type, idempotency_key, payload_signed }、delivered にもう送った冪等キーの集合 { key: 1 } を渡すと、送ってよければ { ok: true, errors: [] }、だめなら { ok: false, errors: [...] } を返します。cfg は allowed_recipients・allowed_events・require_idempotency_key・require_signed_payload。port は無く、送信・再送・署名・receipt の検めは利用側が用意します。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-05",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "配信到達・開封確認 — メッセージングの拡張継手（Tsugite）",
      "summary": "メッセージングの拡張継手（Tsugite）は、配信の証拠の問い合わせ（DeliveryEvidence の要求）を契約の型で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。依頼した・届いた・読まれた・確認されたの各段を別の証拠として扱い、届いたことを業務の受け入れと取り違えません（fail-closed）。",
      "problem": "届いた・読まれたを事実として残すとき、問い合わせた段とは別の段の証拠を受け取ってしまうこと、そして配信の証拠から業務の受け入れを導いてしまうことを防ぎます。",
      "use_when": [
        "届いた・読まれた・確認されたを、段ごとの証拠として基盤に問い合わせ、照合してから記録したいとき。",
        "上の段の確認から、下の段の確認や業務の受け入れを推し量らせたくないとき。",
        "同意や監査のために、依頼しただけの状態と届いた状態を分けて残したいとき。",
        "受領に不整合が出たら、そこで止めたいとき（HALTED）。",
        "同じ問い合わせを二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "受け取った側が処理したか（業務として受け入れたか）を確かめたいとき。この継手は確かめません。",
        "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
        "通知そのものを送りたいとき。送信は D-01 の mail 継手、D-02・D-03 の SMS と push の継手、D-04 の notify 継手が担います。",
        "届いたことを、承認や決裁として扱いたいとき。届いたは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の基盤（どの配信の証拠の仕組みか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "問い合わせた段（evidence_kind）の証拠だけを受理します。別の段の証拠を返す受領は受理せず、停止します。",
        "業務の受け入れを名乗る受領（配信の証拠から業務の受け入れを導くもの）は受理せず、停止します。",
        "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
        "side effect を検めます。配信の証拠の問い合わせは side effect 無し（NONE）です。外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
        "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "配信の証拠を自ら作る仕組みは含みません。配信の証拠の問い合わせを基盤へ運び、戻った受領を照合するだけです。どの配信の証拠の仕組みにつなぐかは未定で、接続するときに別の関門で決めます。",
        "届いた・読まれた・確認されたは、業務の受け入れではありません。受け取った側が処理したかは確かめません。",
        "業務の受け入れの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "届いたは承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に DeliveryEvidence の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には evidence_kind・evidence_state・confirmed・business_acceptance が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は 無し（NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-06",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "バウンス・苦情の記録 — メッセージングの拡張継手（Tsugite）",
      "summary": "メッセージングの拡張継手（Tsugite）は、不達（バウンス）と苦情の記録（BounceComplaint の要求）を契約の型で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合して、止めたか（suppression_state・suppressed）を返す継手（つぎて）です。一時的な保留（DEFERRAL）を止める理由として扱わず、一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "不達・スパム申告（苦情）を記録するとき、一時的な保留まで宛先を止める理由にしてしまうこと、効果が不明な受領や別の要求の受領を受け取ってしまうこと、同じ記録を二度送ることを防ぎます。",
      "use_when": [
        "不達・スパム申告（苦情）を基盤に記録し、止めた状態（suppression_state・suppressed）を受け取りたいとき。",
        "一時的な保留（DEFERRAL）では宛先を止めたくないとき。",
        "止めた宛先を、利用側で送信の継手（mail・SMS と push）へ渡したいとき。この継手は、渡すための状態を返します。",
        "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
      ],
      "do_not_use_when": [
        "送信の停止まで任せたいとき。この継手は状態を返すだけで、止めた宛先を送信の継手（mail・SMS と push）へ渡すのは利用側の役目です。",
        "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
        "止めたことを、承認や決裁として扱いたいとき。どの結果も承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の基盤（どのキュー・発行購読・配信の証拠の仕組みか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "一時的な保留（DEFERRAL）で宛先を止めようとする記録は拒否します。DEFERRAL と、止めない記録（NOT_SUPPRESSED）の side effect は無し（NONE）です。",
        "side effect を検めます。不達・苦情の記録の side effect は NONE か EXTERNAL_REVERSIBLE です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
        "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
        "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "止めた宛先を送信の継手（mail・SMS と push）へ渡しません。この継手は状態を返すだけです。",
        "不達・苦情の記録を運ぶ継手で、苦情の中身の判断や送信の評価（レピュテーション）の管理の機能は含みません。",
        "止める・再送するの方針を決めるのは核（policy-facts）と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "どの結果も承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に BounceComplaint の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には event・suppression_state・suppressed が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は NONE か EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-07",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "メッセージキュー — メッセージングの拡張継手（Tsugite）",
      "summary": "メッセージングの拡張継手（Tsugite）は、キューへの出し入れ（Queue の要求。ENQUEUE・DEQUEUE・ACK）を、pin したキューと冪等キーで検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。二重に積まず、積んだことを届いたことと取り違えません。一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "非同期の受け渡しにキューを使うとき、pin していないキューへ積むこと、同じメッセージを二重に積むこと、同じ冪等キーで別の中身を積むこと、そして積んだことを受け取った側の処理と取り違えることを防ぎます。",
      "use_when": [
        "非同期の受け渡しで、pin したキューにだけ積み（ENQUEUE）・取り出し（DEQUEUE）・ACK を運びたいとき。",
        "同じメッセージを二重に積みたくないとき。ENQUEUE には idempotency_key を必須にし、重複（DEDUP_HIT）は新しい効果なしとして扱います。",
        "受領の状態が操作（op）に対応しているかを確かめてから受け取りたいとき。",
        "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
      ],
      "do_not_use_when": [
        "取り出した側が処理したか（業務として受け入れたか）を確かめたいとき。この継手は確かめません。",
        "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
        "積んだことを、承認や決裁として扱いたいとき。積んだは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の基盤（どのキューの仕組みか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "pin していないキューへの要求は送りません。",
        "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
        "idempotency_key の無い ENQUEUE は送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。",
        "重複（DEDUP_HIT）なのに新しい効果が付いた受領と、DEQUEUE への DEDUP_HIT は受理しません。",
        "操作に対応しない状態の受領は受理しません（ENQUEUE→ENQUEUED か DEDUP_HIT、DEQUEUE→DEQUEUED、ACK→ACKED、または FAILED だけ）。",
        "side effect を検めます。キューの操作は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
        "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "キューそのものは含みません。キューへの出し入れの要求を運ぶ継手です。どの基盤につなぐか、port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "積んだこと・取り出したことは、受け取った側が処理したこと（業務として受け入れたこと）ではありません。確かめません。",
        "重複の検出や、止める・再送するの方針を決めるのは核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "積んだは承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に Queue の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には queue_state が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-08",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "Pub/Sub ファンアウト — メッセージングの拡張継手（Tsugite）",
      "summary": "メッセージングの拡張継手（Tsugite）は、発行と購読（PubSub の要求。PUBLISH・SUBSCRIBE・UNSUBSCRIBE）を、pin した話題（topic）とイベントのスキーマで検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。発行したことを受け取られたことと取り違えません（received: NOT_OBSERVED）。一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "1 対多の配信に発行と購読を使うとき、pin していない話題への発行・購読、スキーマを pin していないイベントの発行、そして発行したことを購読側が受け取ったことと取り違えることを防ぎます。",
      "use_when": [
        "pin した話題にだけ、発行（PUBLISH）・購読（SUBSCRIBE）・購読の解除（UNSUBSCRIBE）を運びたいとき。",
        "発行するイベントに、pin したイベントのスキーマ（event_schema_ref）を必ず付けたいとき。",
        "発行したことと、受け取られたことを分けて扱いたいとき。",
        "同じ発行を二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。",
        "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
      ],
      "do_not_use_when": [
        "購読側が受け取ったか・処理したかを確かめたいとき。received は観測しておらず（NOT_OBSERVED）、業務として受け入れたかも確かめません。",
        "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
        "発行したことを、承認や決裁として扱いたいとき。発行したは承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の基盤（どの発行購読の仕組みか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "pin していない話題、pin していないイベントのスキーマへの要求は送りません。",
        "イベントのスキーマ（event_schema_ref）の無い PUBLISH は送りません。",
        "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
        "操作に対応しない状態の受領は受理しません（PUBLISH→PUBLISHED、SUBSCRIBE→SUBSCRIBED、UNSUBSCRIBE→UNSUBSCRIBED、または FAILED だけ）。",
        "side effect を検めます。発行と購読は EXTERNAL_REVERSIBLE（FAILED は NONE）です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
        "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "発行と購読の要求を基盤へ運ぶ継手で、1 対多に配る（ファンアウト）機能そのものは含みません。",
        "発行したことは、受け取られたことではありません（received: NOT_OBSERVED）。",
        "実の基盤を含みません。どの発行購読の仕組みにつなぐか、port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "業務の受け入れの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "発行したは承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に PubSub の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には pubsub_state・received: NOT_OBSERVED が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "D-09",
      "category": "D/通知・配信",
      "kind": "Tsugite",
      "title": "デッドレター処理 — メッセージングの拡張継手（Tsugite）",
      "summary": "メッセージングの拡張継手（Tsugite）は、デッドレターの操作（DeadLetter の要求。PARK・REDRIVE・PURGE）を、pin したデッドレター（dlq_refs）で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。取り消せない消去（PURGE）は設定 allow_purge: true のときだけ運び、一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "失敗したメッセージの退避にデッドレターを使うとき、pin していないデッドレターを操作すること、設定で許していないのに取り消せない消去（PURGE）をしてしまうこと、そして効果が意図より大きい受領を受け取ってしまうことを防ぎます。",
      "use_when": [
        "失敗したメッセージの退避のために、pin したデッドレターへの PARK・REDRIVE を運びたいとき。",
        "取り消せない消去（PURGE）を、設定で明示して許したときだけ運びたいとき（allow_purge の既定は false）。",
        "同じ操作を二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。",
        "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
      ],
      "do_not_use_when": [
        "消してよいかの判断を任せたいとき。消してよいかの判断は核と人の役目です。",
        "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
        "退避・消去の成功を、承認や決裁として扱いたいとき。どの結果も承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の基盤（どのデッドレターの仕組みか）は含まず、まだ決まっていません。"
      ],
      "what_it_checks": [
        "pin していないデッドレターへの要求は送りません。",
        "設定 allow_purge: true でないときの PURGE は送りません。",
        "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
        "操作に対応しない状態の受領は受理しません（PARK→PARKED、REDRIVE→REDRIVEN、PURGE→PURGED、または FAILED だけ）。",
        "side effect を検めます。PARK・REDRIVE は EXTERNAL_REVERSIBLE、PURGE は EXTERNAL_IRREVERSIBLE、FAILED は NONE です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
        "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "PURGE で消したものは取り消せません（EXTERNAL_IRREVERSIBLE）。この継手は、設定で許したかを検めるだけです。",
        "デッドレターの操作の要求を運ぶ継手で、失敗したメッセージを自ら見つけて退避する機能は含みません。",
        "消してよいかの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "どの結果も承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に DeadLetter の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には dlq_state が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は PARK・REDRIVE が EXTERNAL_REVERSIBLE、PURGE が EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-01",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "定時トリガ（cron） — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。定時トリガ（cron）としては、予定（ScheduleTrigger）の登録・更新・停止の要求を検めて実行基盤のスケジューラへ送り、受領の予定の状態と次の起動を照合します。起動そのものは実行基盤が行います。",
      "problem": "毎日・毎時の起動を外部のスケジューラに任せるとき、pin していない予定や起動の指定を登録してしまうこと、止めた予定に次の起動が残っていると読み違えること、受領を照合しないまま「登録できた」と扱うことを防ぎます。登録できたことを業務の許可と取り違えないための検めです。",
      "use_when": [
        "毎日・毎時の起動の予定を外部のスケジューラに登録・更新・停止するとき、要求を pin した予定（schedule_refs）と起動の指定（trigger_spec_refs）で検めてから送りたいとき。",
        "スケジューラの受領をそのまま信じず、受領の状態が要求（op）に対応するもの（REGISTER→REGISTERED、UPDATE→UPDATED、DISABLE→DISABLED）か、効果のない FAILED かを照合してから扱いたいとき。",
        "同じ要求を二度送りたくないとき。結果が分からなかった送信は照会（lookup）で解きます。",
        "受領が照合できないとき、以後の送信を止めたいとき（HALTED）。"
      ],
      "do_not_use_when": [
        "予定どおりに起動したかを確かめたいとき。この継手が見るのは登録の受領だけで、起動したかどうかは見ません。",
        "スケジューラそのものがほしいとき。実の実行基盤は含まず、どのスケジューラにつなぐかはまだ決まっていません。",
        "予定を登録できたことを、その仕事を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していない予定・起動の指定、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "受領の状態は、要求（op）に対応するもの（REGISTER→REGISTERED、UPDATE→UPDATED、DISABLE→DISABLED）か、効果のない FAILED（副作用なし・次の起動なし）だけを受け取ります。それ以外は受理しません。",
        "止めた・失敗した予定に次の起動（next_fire_ref）が付いた受領を受理しません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "予定どおりに起動したかは見ません（登録の受領だけを見ます）。起動そのものは実行基盤が行います。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に ScheduleTrigger の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に schedule_state・next_fire_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の schedule_refs・trigger_spec_refs。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-02",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "遅延ジョブ — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。遅延ジョブとしては、N 分後に実行する仕事（DelayedJob）の要求を pin したジョブで検めて実行基盤へ送り、受領の積み込みの状態（enqueue_state）と worker の割当て（worker_assignment_ref）を照合します。時間を待って実行するのは実行基盤です。",
      "problem": "リマインドのように後で実行する仕事を外部の実行基盤に預けるとき、pin していないジョブを積むこと、同じ仕事を二重に積むこと、失敗した積み込みに worker が割り当たったと読み違えることを防ぎます。積めたことを業務の許可と取り違えないための検めです。",
      "use_when": [
        "N 分後に実行する仕事を外部の実行基盤に預けるとき、pin したジョブ（job_refs）だけを送りたいとき。",
        "同じ仕事を二重に積みたくないとき。重複（DEDUP_HIT）は新しい効果なしとして受け取り、同じ冪等キーで別の中身は送りません。",
        "実行基盤の受領をそのまま信じず、契約どおりに照合してから積めたとしたいとき。",
        "結果が分からなかった送信を、二度送らずに照会（lookup）で解きたいとき。"
      ],
      "do_not_use_when": [
        "積んだ仕事が予定の時刻に実行されたかを確かめたいとき。この継手が見るのは積み込みの受領だけで、実行されたかは見ません。",
        "時間を待って実行する仕組みそのものがほしいとき。実の実行基盤は含まず、どの仕組みにつなぐかはまだ決まっていません。",
        "積めたことを、その仕事を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していないジョブ、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "同じ冪等キーで別の中身の要求は送りません。重複（DEDUP_HIT）に新しい効果が付いた受領は受理しません。",
        "失敗した積み込みに worker の割当て（worker_assignment_ref）が付いた受領を受理しません。",
        "受領の状態が要求に対応しないとき、受理しません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "積んだ仕事が実行されたかは見ません（積み込みの受領だけを見ます）。時間を待って実行するのは実行基盤です。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に DelayedJob の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に enqueue_state・worker_assignment_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の job_refs。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-03",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "タスクキュー・ワーカー — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。タスクキュー・ワーカーとしては、重い処理のタスク（TaskEnqueue）の要求と中身の bytes を、pin したキューと中身の digest・上限で検めて実行基盤のキューへ送り、受領の積み込みの状態と worker の割当てを照合します。タスクを実行するのは実行基盤です。",
      "problem": "重い処理を後ろで回すためにキューへ積むとき、途中で改ざんされた中身や大きすぎる中身を積むこと、同じタスクを二重に積むこと、失敗した積み込みに worker が割り当たったと読み違えることを防ぎます。",
      "use_when": [
        "重い処理を後ろで回すため、pin したキュー（queue_refs）にだけタスクを積みたいとき。",
        "キューに積む中身（bytes）の digest と上限（max_payload_bytes）を、送る前に自分で確かめたいとき。",
        "同じタスクを二重に積みたくないとき。重複（DEDUP_HIT）は新しい効果なしとして受け取り、同じ冪等キーで別の中身は送りません。",
        "結果が分からなかった送信を、二度送らずに照会（lookup）で解きたいとき。"
      ],
      "do_not_use_when": [
        "積んだタスクが実行されたかを確かめたいとき。この継手が見るのは積み込みの受領だけで、実行されたかは見ません。",
        "キューの仕組みそのものがほしいとき。実の実行基盤は含まず、どのキューにつなぐかはまだ決まっていません。",
        "積めたことを、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していないキュー、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "中身を検めます。要求の payload_digest（対象は task_ref）と中身の bytes が合わないとき（改ざん）は送りません。TaskEnqueue は bytes が必須です。",
        "上限（max_payload_bytes）を超える中身は送りません。",
        "同じ冪等キーで別の中身の要求は送りません。重複（DEDUP_HIT）に新しい効果が付いた受領は受理しません。",
        "失敗した積み込みに worker の割当て（worker_assignment_ref）が付いた受領を受理しません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "積んだタスクが実行されたかは見ません（積み込みの受領だけを見ます）。タスクを実行するのは実行基盤です。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request, bytes) に TaskEnqueue の要求と bytes（必須）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に enqueue_state・worker_assignment_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の queue_refs・max_payload_bytes（必須）。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-04",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "ワークフロー統括（saga） — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。ワークフロー統括（saga）としては、サガの手順（SagaStep）の前進（FORWARD）と補償（COMPENSATE）の要求を、pin したサガと手順で検めて実行基盤へ送り、受領の手順の状態を照合します。サガの調停は実行基盤が行い、補償できたことを人の取消しの承認と取り違えません。",
      "problem": "多段処理が途中で失敗して巻き戻すとき、補償の参照が無い補償を送ること、効果の分からない手順や取り消せない手順を許しなく送ること、補償できたことを人の取消しの承認として扱ってしまうことを防ぎます。",
      "use_when": [
        "多段処理の手順を、pin したサガ（saga_refs）と手順（step_refs。補償の手順も含む）だけで送りたいとき。",
        "途中失敗の巻き戻しで、補償の要求（COMPENSATE）を補償の手順の参照つきでだけ送りたいとき。",
        "受領の手順の状態が要求に対応するか（FORWARD→EXECUTED・FAILED・SKIPPED、COMPENSATE→COMPENSATED・FAILED・SKIPPED）を照合したいとき。",
        "取り消せない効果の手順を、設定で明示したとき（allow_irreversible: true）だけ送りたいとき。既定は false です。"
      ],
      "do_not_use_when": [
        "補償できたことを、人の取消しの承認として扱いたいとき。承認は核と人の役目で、受領からは NOT_DERIVED だけを受け取ります。",
        "手順を実行できたことを、業務の許可として扱いたいとき。業務の許可を導く受領は受理しません。",
        "サガの調停（多段処理の状態機械）そのものがほしいとき。調停は実行基盤の側で、実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していないサガや手順、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "補償の手順の参照が無い補償（COMPENSATE）は送りません。",
        "意図した効果が UNKNOWN の手順は送りません。取り消せない効果の手順は、設定 allow_irreversible: true のときだけ送ります。",
        "受領の手順の状態が要求に対応しないとき、受理しません。",
        "補償の承認や業務の許可を導く受領を受理しません（NOT_DERIVED だけを受け取ります）。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "サガの調停（多段処理の状態機械）は実行基盤の役目で、この継手は持ちません。巻き戻しそのものも行わず、補償の要求を検めて送り、受領を照合するだけです。",
        "補償できても人の取消しの承認ではありません。成功はどれも承認ではなく、どの結果にも approval: false が付きます。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に SagaStep の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に step_state・compensation_authorization・business_authorization を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の saga_refs・step_refs・allow_irreversible。副作用は手順ごとの意図どおり（SKIPPED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-05",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "分散ロック — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。分散ロックとしては、ロック（Lock）の取得（ACQUIRE）・更新（RENEW）・解放（RELEASE）の要求を、pin したロックとこの継手の holder（holder_ref）と上限以内の期限で検めて実行基盤へ送り、受領のロックの状態を照合します。排他そのものは実行基盤の役目で、リーダー選出はしません。",
      "problem": "同時実行の排他を外部のロックの仕組みに任せるとき、他の holder のロックに触ること、期限の無いロックや上限を超える期限のロックを取ること、結果が分からない（UNKNOWN）ときに「持っている」と読み違えることを防ぎます。持っていないロックを持っていると扱わないための検めです。",
      "use_when": [
        "同時実行の排他を外部のロックの仕組みに任せるとき、pin したロック（lock_refs）を、この継手の holder（holder_ref）だけで扱いたいとき。",
        "ロックに期限を付け、期限の上限（max_lock_ttl_seconds）を超えない ACQUIRE・RENEW だけを送りたいとき。",
        "「持っている」（held: true）を、受領の状態が ACQUIRED・HELD のときだけにしたいとき。UNKNOWN からは導きません。",
        "fencing token（fencing_token_ref）を、持っているロック（ACQUIRED・HELD）の受領でだけ受け取りたいとき。"
      ],
      "do_not_use_when": [
        "この継手でリーダー選出（E-06）をしたいとき。この継手の契約にリーダー選出の種類は無く、選出の任期や他の持ち主の観測も持ちません。E-06 は、この継手の分散ロックを束ねる部品(Component)（リーダー選出の部品）が担います。",
        "排他が正しく守られることを、この継手に頼りたいとき。ロックの排他の正しさは実行基盤の役目で、この継手は holder と期限と受領の整合だけを見ます。",
        "ロックの仕組みそのものがほしいとき。実の実行基盤は含まず、どのロックの仕組みにつなぐかはまだ決まっていません。",
        "ロックを持てたことを、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していないロック、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "この継手の holder ではない holder のロックの要求は送りません。他が持っていれば DENIED で、効果なしとして受け取ります。",
        "期限の無い ACQUIRE・RENEW と、上限（max_lock_ttl_seconds）を超える期限の要求は送りません。",
        "受領の状態が要求に対応しないとき（ACQUIRE→ACQUIRED・HELD・DENIED・UNKNOWN、RENEW→HELD・EXPIRED・DENIED・UNKNOWN、RELEASE→RELEASED・EXPIRED・DENIED・UNKNOWN 以外）、受理しません。",
        "解放したロックなど、持っていないロックに fencing token が付いた受領を受理しません。UNKNOWN から「持っている」を導きません。",
        "リーダー選出（E-06）の種類の要求は、この継手の契約に種類が無いので受け付けません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と効果が分からない（UNKNOWN）受領を受理せず、照合できない受領で停止します（HALTED）。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "リーダー選出はしません。E-06 は、この継手を束ねる部品(Component)（リーダー選出の部品）が担います。この継手自身には選出の種類はありません。",
        "ロックの排他の正しさは実行基盤の役目です。この継手は holder と期限と受領の整合だけを見ます。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に Lock の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に lock_state・held・fencing_token_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の lock_refs・holder_ref・max_lock_ttl_seconds。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-06",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Component",
      "title": "リーダー選出 — リーダー選出の部品（Component）",
      "summary": "リーダー選出の部品（Component）は、冗長な構成で 1 つだけを動かすために、E-05 分散ロックの継手を束ねてリーダーを選ぶ部品です。立候補・期限の前の更新・降りるを行い、ロックを握れて fencing token があり、リースの期限（送る前の時刻 + ttl − 安全余裕）より前のときだけ「リーダー」と答えます。拒否・期限切れ・結果不明・障害・時計の逆行では、すぐリーダーでなくなります（fail-closed）。リーダーであることは権限ではありません。",
      "problem": "可用性の裏で、同じロックを取り合う 2 つの参加者が同時にリーダーを名乗ること（ロックの基盤が正しいとき）、受領の遅れの分だけ期限を長く見てしまうこと、fencing token の無いままリーダーを名乗ることを防ぎます。",
      "use_when": [
        "冗長な構成で、処理を 1 つの参加者だけに動かしたいとき。E-05 分散ロックの継手のクライアント（submit(LockRequest) を持つもの）を lock として注入します。",
        "立候補（campaign）・期限の前の更新（renew）・降りる（resign）・今リーダーかの確認（status）を、例外を投げない操作で行いたいとき。",
        "リーダーのときに fencing token（fencing_token_ref）を受け取り、書込み先に渡したいとき。leader が true のときは必ず fencing_token_ref があります。",
        "期限を、ロックを頼む前に読んだ時刻 + ttl − 安全余裕で短く見積もりたいとき。時刻は注入した単調な時計（clock.now()）だけから取ります。",
        "降りたら、ロックの解放の結果によらず、手元ではすぐリーダーでないことにしたいとき。"
      ],
      "do_not_use_when": [
        "リーダーであることを、権限や承認として使いたいとき。どの結果にも approval: false と authority: NOT_DERIVED が付きます。",
        "古いリーダーの書込みを、この部品で止めたいとき。fencing token で古いリーダーの書込みを止めるのは書込み先の役目で、この部品は token を渡すだけです。",
        "「リーダー」を、ロックの基盤の最新の状態そのものとして扱いたいとき。「リーダー」はこの参加者の手元の判定です。",
        "再起動をまたいでリーダーを続けたいとき。状態はこのプロセスの中だけで、再起動の後は立候補し直すまでリーダーでありません。",
        "本物のロックの基盤で確かめた部品が要るとき。本物の基盤には一度も接続しておらず、代役のロックだけで確かめました。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違うとき（ttl が 1〜3600 の外、安全余裕が ttl 以上、submit の無い lock、now の無い clock、.. を含む参照）、INVALID_OPTIONS で止めます。",
        "前の操作が終わっていないとき、2 つ目を BUSY で止めます（同時に 1 つの操作だけ）。",
        "リーダーでないのに更新しようとしたとき NOT_LEADER、期限を過ぎてからの更新のとき LEASE_EXPIRED で、ロックへ送りません。ロックが基盤の側で切れていたときも LEASE_EXPIRED です。",
        "ほかの参加者がロックを握っているとき LOCK_DENIED、結果が DONE でないとき（UNKNOWN・REFUSED・ALREADY_DONE・HALTED など）や held が true でないとき LOCK_UNKNOWN で、リーダーでないとします。",
        "握れたのに fencing token が無いとき NO_FENCING_TOKEN、ロックのクライアントが例外を投げたとき PORT_FAILED で、リーダーを名乗りません。",
        "時計が戻ったとき、リースを捨てて CLOCK_WENT_BACKWARDS で止めます。",
        "期限を延ばすのは、status が DONE で held が true、かつ fencing token があるときだけです。同じロックを取り合う 2 つの参加者が同時にリーダーを名乗らないことを、ファズで毎回確かめました。"
      ],
      "what_it_does_not_do": [
        "リーダー選出の部品は継手ではありません。自分の Request/Receipt は無く、ロックの取得・更新・解放という外への効果は、束ねた E-05 の継手が自分の契約（workflow-jobs/v1）で起こします。この部品自身は外へ触れません。",
        "束ねた E-05 の継手の意味（受領の照合・承認ではない）は、継手の側に残ります。",
        "リーダーであることは権限でも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "fencing token を使った書込みの守りはしません。古いリーダーの書込みを止めるのは書込み先の役目です。",
        "ロックの基盤の正しさ（線形化・時計のずれ・分断のときの振る舞い）は確かめません。安全性は、その正しさと、呼び出し側が決める安全余裕の見積もりに頼ります。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の調整サービスの使い方の雛形ではありません。"
      ],
      "integration": "src/leader.js の createLeaderElection(options) で作ります。受け取るのは lock_ref・holder_ref・ttl_seconds・renew_margin_ms・lock・任意の clock と timestamp で、{ ok, errors, instance } を返します。campaign・renew・resign（Promise）と status は { ok, errors, leader, fencing_token_ref, approval: false, … } を返します。利用側が用意する port は lock.submit(request)（E-05 の継手のクライアント）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-07",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "デバウンス・スロットル — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。デバウンス・スロットルとしては、間引き（Throttle）の要求を pin した間引きの方針で検めて実行基盤へ送り、受領の判定（decision・allowed・retry_after_ref）を照合します。間引きの判定そのものは実行基盤が行います。",
      "problem": "連打や多重起動を抑える判定を外部の実行基盤に任せるとき、pin していない方針で問い合わせること、許可（ALLOW）の判定に再試行の時刻（retry_after_ref）が付いた食い違った受領を受け取ること、受領を照合しないまま判定を使うことを防ぎます。",
      "use_when": [
        "連打・多重起動の抑制の判定を、pin した間引きの方針（throttle_policies）でだけ実行基盤に問い合わせたいとき。",
        "実行基盤の判定をそのまま信じず、要求と受領の一致（request_id・request_digest）を照合してから使いたいとき。",
        "間引きの問い合わせを、外への効果なし（NONE）として扱いたいとき。効果の付いた受領は受理しません。"
      ],
      "do_not_use_when": [
        "間引きの判定そのものをこの継手に期待するとき。判定は実行基盤の役目で、実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
        "デバウンス専用の要求の種類を求めるとき。契約の種類は間引き（Throttle）で、デバウンス専用の種類はありません。",
        "判定の許可（allowed）を、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していない間引きの方針、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "許可（ALLOW）の判定に再試行の時刻（retry_after_ref）が付いた受領を受理しません。",
        "受領の状態が要求に対応しないとき、受理しません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "間引きの判定はしません。判定は実行基盤が行い、この継手は要求を検めて送り、受領の判定を照合するだけです。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に Throttle の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に decision・allowed・retry_after_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の throttle_policies。副作用は無し（NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-08",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Tsugite",
      "title": "ジョブ状態照会 — 仕事の制御継手（Tsugite）",
      "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。ジョブ状態照会としては、ジョブ状態（JobStatus）の照会を pin したジョブで検めて実行基盤へ送り、受領の状態（status）・再試行できるか（retryable）・失敗の参照（failure_ref）を照合します。完了を業務の受け入れと、再試行できるを再試行の許可と取り違えません。",
      "problem": "「終わった?」に答えるためにジョブの状態を実行基盤に尋ねるとき、完了を業務の受け入れと、再試行できるを再試行の許可と取り違えること、たどれない失敗の参照を受け取ることを防ぎます。",
      "use_when": [
        "進捗・完了の確認を、pin したジョブ（job_refs）でだけ実行基盤に問い合わせたいとき。",
        "失敗の参照（failure_ref）を、ai-execution-error の記録へ解決できるときだけ受け取りたいとき。",
        "完了や再試行できるを、業務の受け入れ・再試行の許可と分けて扱いたいとき。受領からは NOT_DERIVED だけを受け取ります。",
        "照会を外への効果なし（NONE）として扱いたいとき。効果の付いた受領は受理しません。"
      ],
      "do_not_use_when": [
        "失敗の記録の中身を検めたいとき。failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけを見ます）。",
        "完了を業務の受け入れとして、再試行できるを再試行の許可として扱いたいとき。どちらも核と人の役目です。",
        "ジョブの状態を持つ仕組みそのものがほしいとき。実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "参照を検めます。pin していないジョブ、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
        "failure_ref が解決できないとき、または解決の手段（port.resolveFailureRef）が無いときは受領を受理せず、以後は停止します（HALTED）。",
        "failure_ref は FAILED・CANCELLED のときだけ受け取ります。",
        "再試行の許可や業務の受け入れを導く受領を受理しません（NOT_DERIVED だけを受け取ります）。",
        "受領の状態が要求に対応しないとき、受理しません。UNKNOWN の状態から実行や許可を導きません。",
        "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
        "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
        "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけを見ます）。",
        "完了は業務の受け入れではなく、再試行できるは再試行の許可ではありません。成功はどれも承認ではなく、どの結果にも approval: false が付きます。",
        "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
        "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラやキューの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に JobStatus の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に status・retryable・failure_ref・retry_authorization・business_acceptance を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup、failure_ref があるとき必須の resolveFailureRef）と cfg の job_refs。副作用は無し（NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-09",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Component",
      "title": "読み取り専用の集計の実行 — 読み取り専用の集計の実行の部品（Component）",
      "summary": "読み取り専用の集計の実行の部品（Component）は、入力を検めて読み、集計の結果を副本として書く部品です。入力はどれも期待する sha256 を添えて C-01 の継手で読み、digest が合ったものだけを JSON として読みます。集計器（fn）には凍った写しを渡します。fn の出力は RFC 8785 の bytes にし、系譜の記録（部品(Primitive) H-10）と一緒に、中身でアドレスしたキーへ冪等書込の継手（C-05）で書きます。fn は同じプロセスの中で動き、安全のための隔離ではありません。",
      "problem": "集計表の数字がどの入力から出たかは、表に出ません。この部品は、入力の集合・集計器の版・出力のハッシュを系譜として残します。あわせて、検めていない入力で集計すること、集計器に保管物を書き換えさせること、出力で入力の場所を上書きすることを防ぎます。",
      "use_when": [
        "1〜1000 個の入力（key と sha256）を検めて読み、信頼できる集計器 fn で集計し、出力を out/<tool ref>/<入力の集合の digest>/<出力の sha256>.json に 1 回だけ書きたいとき（run）。",
        "fn に、キーの順の凍った配列 [{ key, sha256, data }] だけを渡し、元の bytes や読んだ値そのものは渡したくないとき。",
        "出力の隣に、入力の Ref（<key>@sha256:<hex>）・集計器の ref と version・出力のハッシュを結んだ系譜の記録を残したいとき。",
        "ほかの集計器の出力を、次の集計の入力にしたいとき。出力の場所は集計器ごとで、自分の出力の場所と重ならない入力は読めます。",
        "結果の分からなかった書込を、同じ要求で送り直したいとき（resolvePending）。同じ run をもう一度すれば、出力は DEDUP_HIT になります。"
      ],
      "do_not_use_when": [
        "信頼できない集計器を隔離して動かしたいとき。fn は同じプロセスで動き、process・require・グローバルの値・時計・乱数・ネットワークに触れられ、時間とメモリの上限もありません。この部品が守るのは「入力を書き換えさせない」ことだけです。",
        "集計の正しさや、fn が決定的であることを確かめたいとき。系譜は使った入力・集計器の版・出力のハッシュの記録で、集計の正しさの証明ではありません。",
        "fn に小数を返させたいとき。出力は RFC 8785（rfc8785-restricted/v1）なので、小数は受け取りません。",
        "入力・出力の大きさの上限をこの部品に任せたいとき。上限は読出しと書込の継手の設定で決めます。",
        "入力の JSON の重複したキーや、安全な整数を超える数を検めたいとき。JSON.parse のとおりに読み、fn に渡る値として検めません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違うとき INVALID_OPTIONS、run の入力の形が違うとき INVALID_INPUT、同じキーが 2 回以上あるとき DUPLICATE_INPUT、入力のキーが出力の場所 out/<tool ref> と同じかその下にあるとき INPUT_OUTPUT_OVERLAP で、何も読みません。",
        "C-01 の継手が NOT_FOUND を返したとき INPUT_NOT_FOUND、GET_REJECTED を返した・自分で計算し直した sha256 が違うとき INPUT_DIGEST_MISMATCH、結果が分からないとき READ_UNKNOWN、送らなかったとき READ_REFUSED、停止・形の違いのとき READ_FAILED で、fn を呼ばず何も書きません。",
        "入力の bytes が UTF-8 の JSON でないとき（BOM を含む）、INPUT_NOT_JSON で fn を呼びません。",
        "fn が例外を投げたとき（凍った入力を strict mode で書き換えようとした場合を含む）AGGREGATOR_FAILED、fn の値が RFC 8785 で正準化できない・同期の値でないとき OUTPUT_NOT_JSON（reason を添える）で、何も書きません。",
        "H-10 が例外を投げたとき LINEAGE_FAILED、ok: true を返さないとき LINEAGE_REFUSED、記録の形や count・出力の digest と大きさ・tool・recorded_at・schema が今回の実行と合わないとき LINEAGE_INVALID で、何も書きません。",
        "書込の結果が分からないとき WRITE_UNKNOWN で保留にし、保留の間の run を WRITE_PENDING で止めます。送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。",
        "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
      ],
      "what_it_does_not_do": [
        "読み取り専用の集計の実行の部品は継手ではありません。自分の Request/Receipt は無く、書込という外への効果は、束ねた C-05 の継手が自分の契約で起こします（読出しは NONE）。この部品自身は外へ触れません。",
        "束ねた部品の意味（読めた ≠ 中身の正しさ・系譜は集計の正しさの証明ではない・書けた ≠ 受け入れ）は、それぞれの部品の側に残ります。集計器そのものと集計の正しさは、この部品に入りません。",
        "書けたことは、集計の正しさでも承認でもありません（approval: false・authority: NOT_DERIVED・aggregation_correctness: NOT_VERIFIED、derives_authority: false）。",
        "fn を隔離しません（sandbox ではありません）。fn の時間とメモリにも上限を設けません。",
        "C-05 のキーと C-01 の object_ref が同じ保管物を指すか、出力を後で C-01 で読めるかは backend の設定の役目です。",
        "本物の object-store・storage-lifecycle の backend と H-10 の本物のコードには、この部品の試験では接続していません。単体試験とファズは代役です。状態（保留の書込）はプロセスの中だけで、再起動で消えます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の集計ツール・BI の雛形ではありません。"
      ],
      "integration": "src/sandbox-run.js の createSandboxRun(options) で作り、{ ok, errors, instance } を返します。options は output_target・request_prefix・ports・任意の timestamp です。run({ inputs, aggregator: { ref, version, fn } }) は、書けたとき output_key・lineage_key・written などを添え、結果に approval: false が付きます。port は read.get（C-01 の継手のクライアント）・lineage（H-10 の recordLineage）・write.submit（C-05 の継手のクライアント）です。副作用は束ねた継手が起こし、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "E-10",
      "category": "E/スケジュール・ワークフロー",
      "kind": "Primitive",
      "title": "安全側の長さの写し — 安全側の長さの写しの部品（Primitive）",
      "summary": "安全側の長さの写しの部品（Primitive）は、P-05（制約・割当・時間枠）へ渡す前に、工程の見積りの幅 duration { min, max } を上限 { min: max, max: max } に写す単独の部品です。{ unknown: true } は変えず、ほかの欄はそのまま写します。元の min・max を projected に残し、変換の profile と写した入力の digest を返します。丸めず・詰めず、範囲の外と形の違う値は決まったコードと位置で止めます（fail-closed）。P-05 は呼びません。",
      "problem": "予定が「入る」と出た裏で、楽観の長さ（min）で置いたかは見えません。P-05 は工程を duration.min の長さで置くので、この写しを通すと max の長さで置かれます。たとえば不可分の 10〜20 分の工程は 20 分として扱われ、10 分の枠には入りません。どちらの長さで判定したかは、projection_profile と projected_tasks_digest で記録できます。",
      "use_when": [
        "工程の並び（0〜200 件）の duration を、見積りの上限 max で置く形に写してから P-05 に渡したいとき（projectDurations）。どの工程も { min, max } なら { max, max } になり、min = max ならそのままです。",
        "どの工程の長さを変えたかを、元の値つきで残したいとき。projected に、変えた工程だけを { id, from: { min, max }, to: { min, max } } で並びの順に載せます。",
        "どの入力を P-05 に渡したかを後から照らしたいとき。写した tasks の正準の JSON（キーを UTF-16 の符号単位の順、余白なし）の UTF-8 の sha256 を projected_tasks_digest として返します。",
        "長さの分からない工程（{ unknown: true }）を推し量らずにそのまま残し、その id を unknown に並べて受け取りたいとき。",
        "工程のほかの欄（requires・resource・window など）を、値として同じまま新しいデータに写したいとき。入力は書き換えません。",
        "例外を投げず、状態を持たない処理として、手元で呼びたいとき。時計・乱数・環境の locale を使わず、同じ入力には同じ答えを返します。"
      ],
      "do_not_use_when": [
        "楽観の長さ（min のまま）で判定したいとき。その判定はこの部品を通しません。P-05 が自分で max を使うのではなく、使う側がこの写しを明示して通します。",
        "配置の可否を知りたいとき。この部品は P-05 を呼ばず、配置の可否を言いません。P-05 の答え（feasible など）は P-05 の事実のままです。",
        "工程の形（requires・resource・window など）を検めたいとき。そのまま写すだけで、検めるのは P-05 です。",
        "見積りの max が正しいかを確かめたいとき。この部品は見ません。",
        "200 件を超える工程、100,000,000 分を超える長さ、32 段を超える入れ子の欄を扱いたいとき。決まったコードで止めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "tasks が素の配列でない（配列の派生クラスを含む）とき INVALID_TASKS、200 件を超えるとき TOO_MANY_TASKS で止めます。",
        "id が無い・getter・1〜128 の符号単位の文字列でない・単独のサロゲートがあるとき INVALID_ID、同じ id が 2 回あるとき DUPLICATE_ID（2 回目の位置）で止めます。",
        "duration が無い・getter、{ min, max }（ほかのキーなし）か { unknown: true } でない、min・max が整数でない・範囲の外、min が 1 より小さい、max が min より小さいとき、INVALID_DURATION で止めます。丸めず・詰めません。",
        "要素が素のオブジェクトでない（穴・null・継いだもの）、symbol・単独のサロゲートのキー、ほかの欄に getter・関数・undefined・整数でない数・安全でない整数・Date などの素でないオブジェクト・穴、入れ子 32 段を超えるとき、MALFORMED_TASK で止めます。",
        "値を調べること自体が例外になるとき（Proxy の罠など）、例外を出さずに INSPECTION_FAILED で止めます。getter は呼びません。",
        "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、写した tasks は返しません。",
        "結果を、別の書き方の独立オラクル（isWellFormed、比較関数での並べ替え、配列の線形の探索）と突き合わせるファズ（1,000,000 回）で、写した tasks・projected・unknown・digest と止めるコード・path の不一致、fail-open が 0 であることを確かめました。",
        "本物の P-05（solveSchedule）に不可分の 10〜20 分の工程と 15 分の枠を渡すと、そのままでは feasible、この部品で写した後は infeasible（INDIVISIBLE_TOO_LONG）になることを、手元で 1 例だけ確かめました。"
      ],
      "what_it_does_not_do": [
        "P-05 との結線は手元で 1 例だけ確かめました。部品の束は P-05 を含まないので、単体試験には入っていません。",
        "写すのは duration だけです。見積りの max が正しいかも、工程のほかの欄の形も検めません。",
        "配置はしません。写した結果は配置の可否ではなく、承認でもなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
        "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。P-05 の契約は変えていません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のスケジューラーの使い方の雛形ではありません。"
      ],
      "integration": "src/duration-projection.js の projectDurations(tasks) を呼びます（投げません。入力を書き換えません）。受け取るのは { id, duration, ... } の素のオブジェクトの素の配列（200 件まで）です。返すのは { ok: true, errors: [], projection_profile, tasks, projected, unknown, projected_tasks_digest } か { ok: false, errors: [{ code, path }] }。写した tasks を P-05 に渡すのは使う側です。副作用・状態・外の接続・資格情報は無く、依存は Node.js の組み込みの crypto だけで、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-01",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "レート制限・クォータ — レート制限・クォータの部品（Primitive）",
      "summary": "レート制限・クォータの部品（Primitive）は、キーごとのトークンバケット（token bucket）で、呼び出しの回数や量を抑える単独の部品です。バケットの量は整数で持ち、小数を使わずに補充します。足りなければ RATE_LIMITED と、待てばちょうど取れるようになる最短の retry_after_ms を返し、状態を変えません（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
      "problem": "「制限に達しました」を返す裏側で、1 つのプロセスの中の呼び出しの速さを抑えるときに、丸めで余計に通すこと、足りないのに量を減らしてしまうこと、知らないキーを無限に覚えてメモリーを使い切ることを防ぎます。",
      "use_when": [
        "1 つのプロセスの中で、キーごとに呼び出しの回数や量を抑えたいとき（take(key, n)）。",
        "止めたときに、待てばちょうど通る最短のミリ秒（retry_after_ms）を返したいとき。1 ms 前では通りません。",
        "バケットを減らさずに残りを見たいとき（peek は新しいキーを作りません）。キーを忘れて満杯に戻したいとき（reset）、満杯のバケットだけを忘れたいとき（prune）。",
        "時計を注入して、試験で結果を決定的にしたいとき。注入した時計の値の列が同じなら、同じ操作の列は同じ結果になります。",
        "例外を投げない呼び方が要るとき。ファクトリーとインスタンスのメソッドは例外を投げません。"
      ],
      "do_not_use_when": [
        "複数のプロセス・マシンで数を共有するレート制限が要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、ほかのプロセス・インスタンス・マシンとは共有しません。分散したレート制限ではありません。",
        "再起動をまたいで数を残したいとき。プロセスを再起動すると数は消え、すべてのキーが満杯に戻ります。",
        "プロバイダーのクォータ・上限を問い合わせたり変えたりしたいとき。それは外の境界で、別の継手の役目です。",
        "時間の窓で数える方式（固定窓・スライド窓）や、キーごとに違う上限が要るとき。",
        "スレッド（worker_threads）の間で 1 つのインスタンスを共有したいとき。1 つのイベントループの中で使う前提です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "トークンが足りないとき、RATE_LIMITED と remaining・retry_after_ms を返し、バケットを変えません。",
        "options がオブジェクトでない・読むと例外が起きるとき INVALID_OPTIONS、知らないオプションのとき UNKNOWN_OPTION、型・範囲が違うとき INVALID_OPTION（field に名前）で止めます。書き間違いを黙って無視しません。",
        "空から満杯までの時間が安全な整数のミリ秒を超えるとき、retry_after_ms を正確に返せないので REFILL_TOO_SLOW で止めます。",
        "key が 1〜256 文字の文字列でないとき INVALID_KEY、n が 1 以上の安全な整数でないとき INVALID_AMOUNT、n が capacity を超えるとき REQUEST_TOO_LARGE で止めます。",
        "新しいキーが max_keys を超えるとき、状態を作らずに TOO_MANY_KEYS で止めます。",
        "時計が受け取った一番新しい時刻より前に戻ったとき CLOCK_WENT_BACKWARDS、clock.now() が例外を投げたとき CLOCK_FAILED、安全な整数を返さないとき INVALID_CLOCK_VALUE で止めます。"
      ],
      "what_it_does_not_do": [
        "複数のプロセス・マシンの間で数を共有しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。分散したレート制限（共有の記憶が要るもの）はこの部品ではありません。",
        "名前の「クォータ」のうち、プロバイダーのクォータ・上限の問い合わせや変更はしません。それは外の境界で、別の継手の役目です。この部品はプロセスの中の数え方だけです。",
        "プロバイダーの API のクォータ・レート制限の数え方と一致するとは確かめていません。",
        "進みすぎた時計（未来の値）は見分けられません。既定の時計（process.hrtime.bigint）の単調さは Node.js に任せています。",
        "誰にどれだけ許すかという業務の意味は決めません。それは Kernel と使う側の役目です。",
        "通したことは、その呼び出しを行ってよいという許可でも、プロバイダーの側のクォータが残っていることの証明でもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のレート制限のライブラリやサービスの使い方の雛形ではありません。"
      ],
      "integration": "src/rate-limit.js の createRateLimiter(options) を呼びます（投げません）。options は { capacity, refill_tokens, refill_interval_ms, max_keys, clock } だけで、clock は任意。返すのは { ok, errors, instance }。instance の take(key, n) は { ok, errors, remaining, retry_after_ms }、peek・reset・prune は remaining・removed・keys を返します。止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリー、依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-02",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "サーキットブレーカ — サーキットブレーカの部品（Primitive）",
      "summary": "サーキットブレーカの部品（Primitive）は、直近の件数の窓で失敗を数え、CLOSED → OPEN → HALF_OPEN → CLOSED／OPEN と移る単独の部品です。呼ぶ前に allow() で聞き、結果を record({ outcome }) で渡します。失敗かどうかは呼ぶ側が決めます。対応する allow の無い結果は UNKNOWN_TRIAL で止めて数えません（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
      "problem": "1 つのプロセスの中で、失敗の続く相手への呼び出しを一時止めるときに、前の呼び出しの遅れた結果や前の回の試しで HALF_OPEN が勝手に閉じてしまうこと、止めたときに状態が変わってしまうことを防ぎます。",
      "use_when": [
        "1 つのプロセスの中で、失敗の続く相手への呼び出しを一時止めたいとき。呼ぶ前に allow() で聞き、結果を record で渡します。",
        "直近 window_size 件の中の FAILURE の件数（failure_threshold 件以上）で OPEN にしたいとき。",
        "OPEN の間に、次に試せるまでのちょうどのミリ秒（retry_after_ms）を返したいとき。",
        "HALF_OPEN の試しを trial_id で結びつけ、どの結果がどの試しのものかを取り違えたくないとき。",
        "時計を注入して、試験で結果を決定的にしたいとき。乱数を使いません（trial_id は通し番号）。",
        "例外を投げない呼び方が要るとき。ファクトリーとインスタンスのメソッドは例外を投げません。"
      ],
      "do_not_use_when": [
        "複数のプロセス・マシンで状態を共有するブレーカが要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、ほかのプロセス・インスタンス・マシンとは共有しません。",
        "再起動をまたいで OPEN を保ちたいとき。プロセスを再起動すると CLOSED の空の窓に戻り、相手がまだ落ちていても数え直します。",
        "時間の窓（直近 N 秒）・失敗の率・遅い呼び出しで数えたいとき。件数の窓だけです。",
        "HALF_OPEN で複数の試しの成功を待ってから閉じたいとき。最初に届いた試しの結果で決めます。",
        "試しの結果を必ず record できないとき。試しの期限は持たず、結果が来ないと HALF_OPEN_BUSY のまま次の試しを出しません。",
        "スレッド（worker_threads）の間で 1 つのインスタンスを共有したいとき、または Node.js v24.21.0 のほかの版で使うとき（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "OPEN の間、allow() は CIRCUIT_OPEN と retry_after_ms を返します。1 ms 前では試しを出しません。",
        "対応する allow の無い結果（HALF_OPEN・OPEN で trial_id が無い、知らない trial_id、前の回や無効になった試しの trial_id、CLOSED で trial_id がある）のとき、数えずに UNKNOWN_TRIAL で止め、状態を変えません。",
        "この回の試しを half_open_max_trials 件出し終えて結果がまだ来ていないとき、HALF_OPEN_BUSY で止めます。",
        "record の引数が { outcome, trial_id? } だけのオブジェクトでない・trial_id が文字列でないとき INVALID_RECORD、outcome が \"SUCCESS\"・\"FAILURE\" でない（大文字小文字も区別）とき INVALID_OUTCOME で止めます。",
        "options がオブジェクトでないとき INVALID_OPTIONS、知らないオプションのとき UNKNOWN_OPTION、型・範囲が違うとき（failure_threshold が window_size を超えるものを含む）INVALID_OPTION で止めます。",
        "時計が受け取った一番新しい時刻より前に戻ったとき CLOCK_WENT_BACKWARDS、clock.now() が例外を投げたとき CLOCK_FAILED、安全な整数を返さないとき INVALID_CLOCK_VALUE で止めます。"
      ],
      "what_it_does_not_do": [
        "複数のプロセス・マシンの間で状態を共有しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。共有するブレーカはこの部品ではありません。",
        "名前の「障害の連鎖を止める」のうち、どの結果を失敗とみなすか（例外・応答を見た分類）はしません。呼ぶ側が決めた SUCCESS／FAILURE だけを数えます。",
        "止めたときに何をするか（F-07 の代わりの候補・F-03 の再試行など）は決めず、呼び出しそのものもしません。使う側の役目です。",
        "state() は時計を読みません。open_ms たった後も、allow() を呼ぶまで OPEN を返します。",
        "進みすぎた時計（未来の値）は見分けられません。既定の時計（process.hrtime.bigint）の単調さは Node.js に任せています。ほかのサーキットブレーカのライブラリの移り方・既定値と同じだとは言いません。",
        "allow() が通したことは、その呼び出しを行ってよいという許可でも、相手が回復したことの証明でもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のサーキットブレーカのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/circuit-breaker.js の createCircuitBreaker(options) を呼びます（投げません）。options は failure_threshold・window_size・open_ms・half_open_max_trials・clock（任意）だけ。返す instance の allow() は state・trial_id?・retry_after_ms?、record({ outcome, trial_id? }) は state、state() は窓の件数・opened_at などを返します。どれも { ok, errors } を持ち、止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-03",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "リトライ（指数バックオフ） — リトライ（指数バックオフ）の部品（Primitive）",
      "summary": "リトライ（指数バックオフ）の部品（Primitive）は、1 つの操作の再試行を数え、次の試行の時刻を決める単独の部品です。遅延は上限つきの指数バックオフ（exponential backoff）を整数の ms で計算し、jitter \"full\" では注入された乱数で選びます。操作は実行せず、待ちもしません。冪等でない（idempotent: false）操作の再試行は NOT_IDEMPOTENT で止めます（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
      "problem": "「たまに直る」一時失敗を再試行するときに、冪等でない操作を再試行してしまうこと、idempotent の言い忘れを通してしまうこと、無限の再試行や遅延の桁あふれを起こすこと、知らないキーを黙って無視することを防ぎます。",
      "use_when": [
        "1 つの操作の再試行の回数（max_attempts。最初の試行を含む）と、次の試行の時刻（not_before_ms）を、1 つのプロセスの中で管理したいとき。",
        "上限つきの指数バックオフの遅延 min(max_ms, base_ms × multiplier^(attempt-1)) を、整数の ms で計算したいとき（computeDelay は状態を持たない純関数です）。",
        "jitter \"full\" で、[0, 遅延] の整数の遅延を注入した乱数で選びたいとき。",
        "冪等でない操作は、最初の試行だけを許し、再試行を止めたいとき（idempotent: false）。",
        "時計と乱数を注入して、試験で結果を再現したいとき。",
        "例外を投げない呼び方が要るとき。工場・computeDelay・インスタンスの操作は例外を投げません。"
      ],
      "do_not_use_when": [
        "複数のプロセス・インスタンスの再試行の合計を数えたいとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、プロセスやインスタンスをまたいで共有しません。",
        "再起動をまたいで試行の回数を残したいとき。状態はプロセスが終われば消えます。",
        "操作の実行や、not_before_ms までの待ち（タイマー）を部品に任せたいとき。どちらも呼ぶ側の役目です。",
        "Equal Jitter・Decorrelated Jitter や、整数でない multiplier（1.5 など）が要るとき。jitter は none と full だけ、multiplier は整数だけです。",
        "予測できない乱数の質が要るとき。注入した乱数は、戻り値が範囲の整数かだけを検めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "idempotent: false の操作で RETRYABLE_FAILURE を記録した後は HALTED になり、次の試行を NOT_IDEMPOTENT で止めます（回数が残っていても）。idempotent が無い・真偽値でないときは INVALID_IDEMPOTENT で止めます（既定値を置きません）。",
        "not_before_ms より前の試行のとき TOO_EARLY（not_before_ms と retry_after_ms を付けて）、試行の回数が max_attempts に達したとき RETRIES_EXHAUSTED で止めます。",
        "試行と記録が交互でないとき（試行中の nextAttempt は ATTEMPT_IN_FLIGHT、試行の無い record は NO_ATTEMPT_IN_FLIGHT）、終わった後の試行・記録は RUN_FINISHED で止めます。",
        "record の input が { outcome } だけでない、または outcome が SUCCESS・RETRYABLE_FAILURE・FINAL_FAILURE でないとき、INVALID_OUTCOME で止めます。",
        "options・policy が素のオブジェクトでないとき INVALID_OPTIONS、知らないキーのとき UNKNOWN_OPTION（key を付けて）、attempt・base_ms・max_ms・multiplier・jitter・max_attempts が範囲の外のとき、それぞれの INVALID_* で止めます。",
        "random が関数でないとき INVALID_RANDOM、[0, 遅延] の整数を返さない・例外を投げたとき RANDOM_OUT_OF_RANGE で止めます。",
        "clock が now() を持たないとき INVALID_CLOCK、clock.now() が 0 以上の安全な整数を返さない・例外を投げたとき CLOCK_INVALID、前に読んだ値より戻ったとき CLOCK_WENT_BACKWARDS で止めます。",
        "止めた操作（ok: false）は、試行の回数・段階・not_before_ms を変えません。"
      ],
      "what_it_does_not_do": [
        "状態はこのプロセスのメモリーの中だけで、プロセスやインスタンスの間で共有せず、再起動で消えます。複数のプロセスにまたがる再試行の数え方には使えません。",
        "名前の「一時失敗の再試行」のうち、再試行の実行はしません。操作の実行と not_before_ms までの待ち（タイマー）は呼ぶ側です。",
        "失敗が一時的か（RETRYABLE か FINAL か）、業務として再試行してよいかは決めません。idempotent と outcome は呼ぶ側の申告をそのまま使い、正しいかを確かめません。",
        "相手の Retry-After やレート制限の応答は読みません（継手の側）。全体の待ち時間の制限は F-06、呼ぶこと自体を止めるのは F-02 の役目です。",
        "時計の単調さは、戻ったときに止めるだけで、保つのは注入した側です。ファズの手本は実装と同じ書き手が同じ仕様から別に書いたもので、仕様の読み違いが両方にあれば見つかりません。",
        "nextAttempt が ok でも、操作を実行してよいという許可ではありません。retry_allowed: true は業務として再試行すべきかの判断ではありません（result_is_approval: false、derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のリトライのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/retry.js の createRetryRun(options) を呼びます（投げない工場）。options は max_attempts・base_ms・max_ms・multiplier・jitter・idempotent（必須）・clock・random だけ。返す instance の nextAttempt() は attempt・not_before_ms、record({ outcome }) は state・retry_allowed、snapshot() は状態を返します。computeDelay(policy, random) は delay_ms を返す純関数です。どれも { ok, errors } を持ち、止めたときは code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-04",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "冪等キー処理 — 冪等キー処理の部品（Primitive）",
      "summary": "冪等キー処理の部品（Primitive）は、新しい要求（段・op_id・中身の digest）を、渡された台帳（前に受けた要求と、その結果の digest）と比べて、はじめて（NEW）・同じものの送り直しで結果あり（DUPLICATE_SAME）・同じものの送り直しで結果がまだ無い（DUPLICATE_PENDING）・同じ ID の別の中身（CONFLICT）のどれかに分ける単独の部品です。段は REQUEST・ATTEMPT・DELIVERY で、段ごとに別に数えます。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
      "problem": "再送で同じ処理が二重に動くことを防ぐための見分けです。同じ ID で中身の違う要求を、同じものの送り直しとして通すこと、結果の分からない送り直しを新しい実行として扱うこと、形の違う台帳の行を黙って読み飛ばすことを防ぎます。",
      "use_when": [
        "新しい要求が、渡された台帳にある要求の送り直しかどうかを、冪等の鍵（段・op_id）と中身の digest（payload_digest）の完全一致で見分けたいとき（classify）。",
        "同じものの送り直しに、前の結果の digest を返したいとき。DUPLICATE_SAME の result_digest は、台帳のその行の result_digest そのものです。",
        "結果がまだ無い行（result_digest が null）への送り直しを、もう一度動かさない印 DUPLICATE_PENDING として受け取りたいとき。もう一度動かす判断にはせず、照合を待つための印です。",
        "同じ ID で中身が違う要求を CONFLICT として見分けたいとき。同じ ID の中身の違いを見ずに、ID だけで重複を決める見分けとは違います。",
        "要求の受付（REQUEST）・試行（ATTEMPT）・届け（DELIVERY）を段ごとに別に数えたいとき。段が違えば、同じ op_id でも比べません。counts に段ごとの別々の op_id の数を返します。",
        "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。同じ入力には同じ答えを返します。"
      ],
      "do_not_use_when": [
        "台帳の保存・読み出しや、ほかの呼び出し・保存済みの台帳との重複を見たいとき。比べるのは引数の中だけで、台帳を保存・読み出すのは Host など、この部品の外の側です。",
        "同時に来た 2 つの要求の競り合いを決めたいとき。どちらを先にするかは、台帳を書く側（Host）が決めます。",
        "要求を断る・記録する・知らせる、または送り直しを実行したいとき。CONFLICT は断る要求の印を返すだけで、断る・記録する・知らせるのは使う側の仕事です。送り直しの実行・時計・保存もしません。",
        "見た目が同じ op_id（é と e + 結合文字、A と a、前後の空白の違い）を同じものとして扱いたいとき。op_id は UTF-16 の符号単位の完全一致だけで比べます。",
        "digest を大文字・sha256: の接頭辞つきで渡したいとき。digest は変換せず、小文字 16 進 64 桁のほかは INVALID_DIGEST で止めます。中身から digest を作る規則（正準化 I-08、ハッシュ B-07 など）は使う側が決め、台帳と要求の両方を同じ規則で作ります。",
        "100,000 行を超える台帳を 1 回で扱いたいとき。使う側で分けます。分けると、分けた先の行との重複は見分けません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "request が素のオブジェクトでない（継いだもの・クラスのもの・null の原型を含む）、stage・op_id・payload_digest が自分のデータのプロパティでない（無い・getter）とき、MALFORMED_REQUEST で止めます。",
        "ledger が素の配列でないとき INVALID_LEDGER、100,000 行を超えるとき TOO_MANY_ENTRIES、行が素のオブジェクトでない（穴・getter の行を含む）か 4 つの欄が自分のデータのプロパティでないとき MALFORMED_ENTRY で止めます。",
        "stage が REQUEST・ATTEMPT・DELIVERY のどれでもない（小文字・箱の文字列を含む）とき INVALID_STAGE、op_id が文字列でない・空のとき INVALID_OP_ID、256 の UTF-16 の符号単位を超えるとき OP_ID_TOO_LONG で止めます。",
        "payload_digest が小文字 16 進 64 桁の文字列でない（大文字・接頭辞・桁違い・16 進でない・箱の文字列・配列・null）、result_digest がその形の文字列でも null でもないとき、INVALID_DIGEST で止めます。",
        "台帳に同じ（段・op_id）の行が 2 回あるとき、中身が同じでも DUPLICATE_LEDGER_ENTRY（2 回目の位置）で止めます。値を調べること自体が例外になるとき（Proxy の罠など）は INSPECTION_FAILED です。",
        "__proto__・constructor などの名前も、ただの op_id として比べます（Map で比べ、オブジェクトの名前に使いません）。getter は呼びません。",
        "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、decision などは返しません。",
        "結果を、Map も Set も使わずに台帳を前から順に探す書き方の独立オラクルと突き合わせるファズ（1,000,000 回）で、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "NEW は「渡された台帳に無い」だけで、実行してよいという意味ではありません。渡されていない台帳（ほかの場所・保存前のもの）との重複は分かりません。",
        "中身の digest が本当に中身から作られたかは見ません（渡された digest を比べるだけ）。DUPLICATE_SAME の結果の digest が正しいかも見ません。",
        "上限ちょうどの 100,000 行を受け取ることは、単体試験だけで確かめました（ファズは上限を超えるものだけ）。",
        "外の境界・状態・時計・乱数を持たず、保存もしません。副作用は NONE で、資格情報も使いません。",
        "見分けの結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の冪等キーのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/idempotency-ledger.js の classify(request, ledger) を呼びます（投げません。入力を書き換えません）。受け取るのは { stage, op_id, payload_digest } の素のオブジェクトと、{ stage, op_id, payload_digest, result_digest } の行の素の配列（100,000 行まで）です。返すのは { ok: true, errors: [], decision, matched_index, result_digest, counts } か { ok: false, errors: [{ code, path }] }。副作用・状態・外の接続・資格情報は無く、依存は Node.js の組み込みだけで、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-05",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "同時実行制限（bulkhead） — 同時実行制限（bulkhead）の部品（Primitive）",
      "summary": "同時実行制限（bulkhead）の部品（Primitive）は、区画（compartment）ごとに、同時に動かす数（max_concurrent）と待ち行列の長さ（max_queue）を制限する単独の部品です。空きがあれば token、満員で待ち行列に空きがあれば ticket を渡し、どちらも満杯なら BULKHEAD_FULL で止めます（fail-closed）。制限はインスタンスごと・このプロセスのメモリーの中だけで、分散の制限ではありません。",
      "problem": "1 つのプロセスの中で、一部の仕事の暴走が同じプロセスのほかの仕事を巻き添えにしないように区画を分けるときに、上限を超えて枠を渡すこと、二重の release や偽の token で枠を余計に空けてしまうことを防ぎます。",
      "use_when": [
        "1 つのプロセスの中で、名前で区切った区画ごとに同時に動かす数と待ち行列の長さを制限したいとき。区画どうしは独立で、ある区画が満杯でもほかの区画に影響しません。",
        "満員のときに、待ち行列に入れて FIFO で繰り上げたいとき。release の結果の promoted に繰り上がった token が入り、待っている側は poll で知ります。",
        "待たずにすぐ断りたい区画があるとき（max_queue 0）。",
        "待ち行列から外したいとき（cancel）。cancel した ticket は繰り上がりません。",
        "例外を投げない呼び方が要るとき。工場とインスタンスの操作は例外を投げません。"
      ],
      "do_not_use_when": [
        "複数のプロセス・マシンをまたぐ同時実行の制限（分散のセマフォ・共有の数え上げ）が要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、プロセスやインスタンスをまたいで共有しません。",
        "再起動をまたいで枠の数を残したいとき。状態は再起動で消えます。",
        "待ちの期限（タイムアウト）が要るとき。待ちすぎを止めるのは F-06 か呼ぶ側です。",
        "待っている側を通知・Promise で起こしたいとき。この部品は待っている側を起こしません。",
        "release を忘れた token の枠を自動で回収したいとき。期限による回収・漏れの検出はありません。",
        "優先度・重み・公平さの調整が要るとき。区画の中は FIFO だけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "区画の枠も待ち行列も満杯のとき、BULKHEAD_FULL（compartment に名前）で止めます。上限を超えて枠を渡しません。",
        "このインスタンスが渡した token そのものでないとき（同じ形の偽物・別のインスタンスの物・ticket との取り違え）UNKNOWN_TOKEN、もう release した token のとき TOKEN_ALREADY_RELEASED で止め、枠を空けません。",
        "このインスタンスが渡した ticket でないとき UNKNOWN_TICKET、cancel 済みのとき TICKET_CANCELLED、繰り上がった後の cancel のとき TICKET_ALREADY_GRANTED で止めます。",
        "acquire の区画名が作った時の区画に無いとき（constructor・toString などのプロトタイプの名前を含む）、UNKNOWN_COMPARTMENT で止めます。",
        "options が素のオブジェクトでないとき INVALID_OPTIONS、知らないキーのとき UNKNOWN_OPTION、区画が 0 個か 257 個以上のとき INVALID_COMPARTMENTS、区画名・区画の値・max_concurrent・max_queue の形や範囲が違うとき、それぞれの INVALID_* で止めます。",
        "clock が now() を持たないとき INVALID_CLOCK、clock.now() が 0 以上の安全な整数を返さない・例外を投げたとき CLOCK_INVALID、前に読んだ値より戻ったとき CLOCK_WENT_BACKWARDS で止めます。",
        "止めた操作（ok: false）は、枠・待ち行列・token・ticket を変えません。"
      ],
      "what_it_does_not_do": [
        "複数のプロセス・マシンの合計は制限しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。分散の制限は外の境界を持つので、この部品ではなく継手の側です。",
        "名前の「一部の暴走を隔離」のうち、暴走の検出や停止はしません。区画ごとに同時に動かす数と待ち行列の長さを制限するだけです。",
        "待ちの期限・待っている側の起こし方・release を忘れた枠の回収は持ちません。",
        "token は同じプロセスの中の取っ手で、所有の証明・権限・実行の許可ではありません。受け取った側がほかへ渡すことは止めません（result_is_approval: false、derives_authority: false）。",
        "ファズの手本は実装と同じ書き手が同じ仕様から別に書いたもので、仕様の読み違いが両方にあれば見つかりません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のセマフォ・プールのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/bulkhead.js の createBulkhead({ compartments, clock? }) を呼びます（投げない工場）。返す instance の acquire(name) は token（queued: false）か ticket（queued: true）、release(token) は compartment・promoted、poll(ticket) は granted と token、cancel(ticket) は compartment、snapshot() は区画ごとの active・queued を返します。どれも { ok, errors } を持ち、止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-06",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "タイムアウト・期限伝播 — タイムアウト・期限伝播の部品（Primitive）",
      "summary": "タイムアウト・期限伝播の部品（Primitive）は、注入された単調な時計（clock.now()、ミリ秒）の上に絶対の期限を作り、親から子へ期限と取消しを伝える単独の部品です。子の期限は親より延びません。取消しはこのプロセスの中の印で、外の処理が止まったことは意味しません。受け取れない値・戻った時計は決まったコードで止めます（fail-closed）。状態はこのプロセスのメモリーの中だけです。",
      "problem": "1 つのプロセスの中で処理を重ねて呼ぶときに、子の期限が親より延びてしまうこと、時計が戻って期限切れが取り消されてしまうこと、取消しが子孫に伝わらないことを防ぎ、待ちすぎ（ハング）を読み取れるようにします。",
      "use_when": [
        "1 つのプロセスの中で、処理の期限を作り、呼び出しの先へ残り時間（remaining_ms）を渡したいとき。外の API を呼ぶ継手に残り時間を渡す側の共通の部品になる候補です。",
        "子の期限を min(親の期限, 今 + 自分の timeout) にして、伝播で期限が広がらないようにしたいとき（child）。",
        "自分と子孫すべてに取消しの印をつけたいとき（cancel）。cancel は時計を読まないので、時計が壊れていても取消しはできます。",
        "期限の状態（ACTIVE・EXPIRED・CANCELLED）と、取消しの理由・由来（SELF・ANCESTOR）を読みたいとき（status・expired）。",
        "時計を注入して、試験で結果を決定的にしたいとき。壁時計（Date）・タイマー・乱数は使いません。",
        "例外を投げない呼び方が要るとき。工場も instance のメソッドも例外を投げません。"
      ],
      "do_not_use_when": [
        "プロセスをまたぐ・分散した期限の伝播（ヘッダーで送るなど）が要るとき。状態はこのプロセスのメモリーの中だけで、プロセス間・分散の共有も永続化もしません。",
        "再起動をまたいで期限を残したいとき。プロセスを終えると状態は消えます。",
        "外の処理（相手先の API など）を取り消したいとき。取消しはこのプロセスの中の印だけで、外の取消しは使う側か継手がそれぞれの契約で行います。",
        "期限の時刻に知らせ（タイマー）が要るとき。使う側が読んだときに分かるだけです。",
        "1 つの木を長く持ち、期限を 10,000 個を超えて作るとき。期限を手放す操作は無く、終わった期限もメモリーに残って数に入ります。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "時計の読みが、その木で読んだ一番大きな値より小さいとき、記録を変えずに CLOCK_WENT_BACKWARDS で止めます。一度 EXPIRED になった期限は ACTIVE に戻りません。",
        "期限切れか取消し済みの期限に子を作ろうとしたとき、DEADLINE_ALREADY_OVER で止めます。",
        "timeout_ms が 1〜2,147,483,647 の安全な整数でないとき（0・負・小数・NaN・文字列・上限超え）、INVALID_TIMEOUT で止めます。",
        "clock が now() を持たないとき INVALID_CLOCK、clock.now() が投げた・0〜MAX_CLOCK_MS の有限の数を返さないとき INVALID_CLOCK_READING、parent の木と別の clock を渡したとき CLOCK_MISMATCH で止めます。",
        "parent がこの部品が作った期限の instance でないとき（写しを含む）、INVALID_PARENT で止めます。",
        "選択肢がオブジェクトでない・許されないキーがある・読むと投げるとき INVALID_OPTIONS、reason_code が英大文字で始まる英大文字・数字・_ の 1〜64 文字でないとき INVALID_REASON で止めます。",
        "木の期限の数が 10,000 個（MAX_DEADLINES_PER_TREE）に達したとき、TOO_MANY_DEADLINES で止めます。"
      ],
      "what_it_does_not_do": [
        "状態はこのプロセスのメモリーの中だけで、プロセス間・分散で共有せず、プロセスを終えると消えます。名前の「期限伝播」は、このプロセスの中の親から子への伝播だけで、プロセスをまたぐ伝播（ヘッダーで送るなど）はしません。",
        "名前の「タイムアウト」のうち、処理を止めることはしません。取消しはこのプロセスの中の印（local_only: true）で、外の処理の取消しや、その成否は扱いません。外の取消しは各継手とその契約の側です。",
        "期限の時刻に知らせることはしません（タイマーを持ちません）。",
        "注入した時計が本当に単調かは確かめられません（戻った読みを止めるだけ）。既定の時計の単調さは Node.js の process.hrtime.bigint に依ります。どの継手・ライブラリの期限の形とも互換を確かめていません。",
        "残り時間・期限切れ・取消しは、この部品の時計と印の上の事実だけで、外の処理が止まったこと・終わったこと・やり直してよいことではありません（result_is_approval: false、derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のライブラリ（AbortController など）の使い方の雛形ではありません。"
      ],
      "integration": "src/deadline.js の createDeadline({ timeout_ms, clock?, parent? }) を呼びます（投げない工場）。返す instance の remaining() は remaining_ms・state、expired() は expired・cancelled・over、child({ timeout_ms }) は子の instance、cancel({ reason_code }) は newly_cancelled・local_only: true、status() は state・deadline_at・cancelled を返します。どれも { ok, errors } を持ち、止めたときは code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "F-07",
      "category": "F/耐障害・流量制御",
      "kind": "Primitive",
      "title": "フォールバック — フォールバックの部品（Primitive）",
      "summary": "フォールバックの部品（Primitive）は、外（方針・Owner・使う側）で決めた「並びつきの許可リスト」の通りに、失敗のあと次の候補へ進んでよいかを 1 歩ずつ決める単独の部品です。候補を足さず、並べ替えず、飛ばさず、許しを広げません。候補の呼び出しはせず、id を返すだけです。形の違う許可リスト・報告は決まったコードで止めます（fail-closed）。状態は実行 1 回分・このプロセスのメモリーの中だけです。",
      "problem": "「一部使えるが動く」ように次の候補へ切り替えるときに、決めていない候補へ移ること、並びを飛ばすこと、許されない失敗のあとで切り替えること、断られた後に別のコードで試し直すこと、作った後に許可リストを書き換えられることを防ぎます。",
      "use_when": [
        "AI の provider・model、保存先などの切り替えを、外で決めた並びの通りにだけ行わせたいとき。",
        "失敗のコードごとに、次の候補へ進んでよいかを決めたいとき。next は、失敗したのが今の候補で、そのコードがすぐ次の候補の allowed_after にあるときだけ、次の候補の id を返します。",
        "切り替えの歩数に上限（max_steps、0〜31）を置きたいとき。",
        "時計・乱数・環境を使わない、決定的な判定が要るとき。",
        "例外を投げない呼び方が要るとき。工場も instance のメソッドも例外を投げません。"
      ],
      "do_not_use_when": [
        "複数のプロセス・マシンで実行の状態を共有したいとき。状態は実行 1 回分・このプロセスのメモリーの中の instance だけで、プロセス間の共有も永続化もしません。",
        "再起動をまたいで今の候補・歩数を残したいとき。プロセスを終えると状態は消えます。",
        "どの候補を並べるか・どの失敗で切り替えてよいかを部品に決めさせたいとき。それは外（方針・Owner・使う側）で決めます。",
        "候補の呼び出し・待ち時間・再試行・遮断まで任せたいとき。期限は F-06、再試行は F-03、遮断は F-02 です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "error_code が、すぐ次の候補の allowed_after に無いとき、FALLBACK_NOT_ALLOWED で止め、その回の実行を終えます。断られた後に別のコードで試し直せません。",
        "failed_id が今の候補でないとき（前の候補・後の候補・知らない id）、状態を変えずに OUT_OF_ORDER で止めます。",
        "今の候補が最後のとき NO_MORE_CANDIDATES、歩数が max_steps に達したとき MAX_STEPS_REACHED で止め、その回の実行を終えます。終わった後の next は RUN_ENDED です。",
        "first を 2 回呼んだとき ALREADY_STARTED、first の前に next を呼んだとき NOT_STARTED、next の報告が failed_id と error_code だけのオブジェクトでないとき INVALID_REQUEST で止めます。",
        "candidates が配列でない・空・32 個を超えるとき INVALID_CANDIDATES、id の形が違うとき INVALID_CANDIDATE_ID、同じ id が 2 つあるとき DUPLICATE_CANDIDATE_ID で止めます。",
        "allowed_after の形が違うとき（形の違うコード・重なるコード・64 個超え・最初の候補で空でない・2 番目からの候補で空）INVALID_ALLOWED_AFTER、max_steps が 0〜31 の安全な整数でないとき INVALID_MAX_STEPS、選択肢の形が違うとき INVALID_OPTIONS で止めます。",
        "作った後に呼び出し側が許可リストの配列を書き換えても、結果は変わりません（写しを持ちます）。"
      ],
      "what_it_does_not_do": [
        "状態は実行 1 回分・このプロセスのメモリーの中だけで、プロセスやマシンの間で共有せず、プロセスを終えると消えます。プロセスをまたぐ・分散した実行の状態は扱いません。",
        "名前の「縮退運転」のうち、縮退した動作そのものはしません。provider や model を選ばず・切り替えず・呼び出さず、外で決めた並びの次の id を返すだけです。候補を呼ぶのは使う側と各継手です。",
        "許可リストの中身が正しいか（その候補に切り替えてよいか・費用や扱うデータの範囲が同じか）は判断しません。外で決めたものを守るだけです。",
        "失敗のコードが本当かは確かめられません。コードの意味も解釈せず、文字列の完全一致だけを見ます。",
        "次の候補の id は「外で決めた許可リストの上で次へ進んでよい」ことだけで、その候補を呼んでよい権限・費用の承認・結果の正しさではありません（result_is_approval: false、derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "どの継手・ライブラリのフォールバックの形とも、互換を確かめていません。"
      ],
      "integration": "src/fallback.js の createFallback({ candidates, max_steps }) を呼びます（投げない工場）。candidates は { id, allowed_after } の並び（1〜32 個）です。返す instance の first() は最初の候補 { id }、next({ failed_id, error_code }) は次の候補 { id }・index・step・allowed_by、status() は state（READY・ACTIVE・ENDED）・current・steps・end_code を返します。止めたときは errors の code（error_taxonomy）です。副作用（NONE）・時計・外の接続・資格情報・port は無く、候補を呼び出すのは使う側です。依存は無い純 JS です。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-01",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "決済オーソリ — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、決済オーソリの要求（PaymentAuthRequest）を、pin した支払い方法と、お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（PaymentAuthReceipt）を契約どおりに照合する継手（つぎて）です。どれかが外れれば、送らず、受理しません（fail-closed）。",
      "problem": "決済オーソリを外の決済基盤へ運ぶとき、許していないのにお金が動く操作を送ってしまうこと、pin していない支払い方法を使ってしまうこと、そしてお金が動いたか分からない receipt や業務の承認を名乗る receipt を信じてしまうことを防ぎます。決済の成功を業務の承認と取り違えないための検めです。",
      "use_when": [
        "決済オーソリ（AUTHORIZE）の要求を外の決済基盤へ運び、戻った receipt が op に合う状態（AUTHORIZED）かを照合してから結果として扱いたいとき。",
        "お金が動く操作を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
        "使ってよい支払い方法を、pin した一覧（method_refs。カード番号ではない参照）に限りたいとき。",
        "同じオーソリを二度動かしたくないとき。冪等キーを必須にし、同じ request_id は二度送らず、障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "決済できたことを、業務の承認として扱いたいとき。決済の成功は業務の承認ではありません（business_approval は運びから導きません）。",
        "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
        "オーソリ済みの記録を再起動の後も持ち続けたいとき。記録はこの継手の中だけで、再起動で消えます（永続化は接続するときに別の関門で決めます）。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
        "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "冪等キーを検めます。冪等キーの無いオーソリ、同じキーで別の中身の要求は送りません。",
        "オーソリの記録を検めます。この継手が AUTHORIZED と照合していない支払いの確定、取消の後の確定、二度目の確定は送りません。",
        "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
        "業務の承認を名乗る receipt は受理しません。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "決済の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に PaymentAuthRequest を渡すと、DONE（result に op・auth_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: オーソリの操作は EXTERNAL_IRREVERSIBLE、辞退・保留・失敗などは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-02",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "売上確定・取消 — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、売上の確定（CAPTURE）と取消（VOID）を、オーソリの要求（PaymentAuthRequest）として運ぶ継手（つぎて）です。確定・取消は、この継手が AUTHORIZED と照合し、まだ確定・取消していない支払いだけを送り、戻った receipt（PaymentAuthReceipt）を契約どおりに照合します。どれかが外れれば、送らず、受理しません（fail-closed）。",
      "problem": "オーソリした支払いを確定・取消するとき、オーソリしていない支払いを確定してしまうこと、取消した支払いを確定してしまうこと、同じ支払いを二度確定してしまうこと、そしてお金が動いたか分からない receipt や業務の承認を名乗る receipt を信じてしまうことを防ぎます。確定できたことを業務の承認と取り違えないための検めです。",
      "use_when": [
        "この継手でオーソリ（AUTHORIZED と照合済み）した支払いを、確定（CAPTURE→CAPTURED）または取消（VOID→VOIDED）として外の決済基盤へ運び、戻った receipt が op に合う状態かを照合してから結果として扱いたいとき。",
        "確定を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
        "確定・取消の後に、同じ支払いを二度確定しないようにしたいとき。",
        "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
      ],
      "do_not_use_when": [
        "ほかの経路でオーソリした支払いを確定・取消したいとき。確定・取消は、この継手が AUTHORIZED と照合した支払いだけです。",
        "オーソリ済みの記録を再起動の後も持ち続けたいとき。記録はこの継手の中だけで、再起動で消えます（永続化は接続するときに別の関門で決めます）。",
        "確定できたことを、業務の承認として扱いたいとき。決済の成功は業務の承認ではありません（business_approval は運びから導きません）。",
        "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "オーソリの記録を検めます。この継手が AUTHORIZED と照合していない支払いの確定・取消、取消の後の確定、二度目の確定は送りません（REFUSED）。",
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、確定を含むお金が動く操作は送りません（REFUSED）。",
        "参照を検めます。空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "receipt を照合します。op と合わない状態（CAPTURE なら CAPTURED、VOID なら VOIDED でないもの）、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
        "業務の承認を名乗る receipt は受理しません。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "確定の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "オーソリ済みの支払いの記録は、この継手の中だけに持ちます（再起動で消えます）。永続化はしません（接続するときに別の関門で決めます）。",
        "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に、op が CAPTURE か VOID の PaymentAuthRequest を渡すと、DONE（result に op・auth_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 確定・取消は EXTERNAL_IRREVERSIBLE、辞退・保留・失敗などは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-03",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "冪等課金 — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、冪等な課金の要求（IdempotentChargeRequest）を、冪等キー・pin した支払い方法・お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（IdempotentChargeReceipt）を契約どおりに照合する継手（つぎて）です。同じ冪等キーは重複として扱い、新しい効果を認めません（fail-closed）。",
      "problem": "課金を外の決済基盤へ運ぶとき、再送や重複で二度課金してしまうこと、同じキーで別の中身を送ってしまうこと、重複なのに効果を名乗る receipt を信じてしまうことを防ぎます。課金の成功を業務の承認と取り違えないための検めでもあります。",
      "use_when": [
        "二重課金を防ぐために、課金の要求に冪等キーを必須にし、同じキーの要求を重複（DEDUP_HIT、新しい効果なし）として扱いたいとき。",
        "課金を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
        "戻った receipt が要求に合う課金の状態かを照合してから、結果（charge_state・charged）として扱いたいとき。",
        "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
      ],
      "do_not_use_when": [
        "課金できたことを、業務の承認として扱いたいとき。課金の成功は業務の承認ではありません（business_approval は運びから導きません）。",
        "課金と入金の突き合わせをしたいとき。突き合わせは外の判断と人・組織の役目で、この継手はしません（対になる核は reconcile-facts です）。",
        "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
        "冪等キーを検めます。冪等キーの無い課金、同じキーで別の中身の要求は送りません。",
        "重複なのに効果を名乗る receipt は受理しません。重複（DEDUP_HIT）の効果は NONE です。",
        "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
        "業務の承認を名乗る receipt は受理しません。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "課金の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
        "額・通貨・税・手数料の正しさ、明細の突き合わせは確かめません（外の判断と人・組織の役目です）。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に IdempotentChargeRequest を渡すと、DONE（result に charge_state・charged・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 課金は EXTERNAL_IRREVERSIBLE、重複・辞退・失敗などは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-04",
      "category": "G/決済・資金",
      "kind": "Component",
      "title": "請求書生成 — 請求書生成の部品（Component）",
      "summary": "請求書生成の部品（Component）は、1 枚の請求書を、税の事実の計算（部品(Kernel) G-07）→ 文書（継手 J-01 の PDF_RENDER）→ 保存（継手 C-05、請求書番号を冪等キー）→ 記録（継手 H-01 の監査台帳）の段で発行する部品です。G-07 の事実に足りない入力や記載事項の欠けがあれば、文書を作らずに事実だけを返します。段の結果が分からないときはその段で止め、同じ番号・同じ中身の issue でその段から続けます（fail-closed）。請求書番号は呼び出し側が決めて渡します。",
      "problem": "会計の裏で、記載事項の欠けや足りない入力のある請求書が文書・保存・台帳に進んでしまうこと、同じ番号で別の中身の請求書を発行してしまうこと、保存を二重にしてしまうこと、請求書の中身を台帳へ渡してしまうことを防ぎます。",
      "use_when": [
        "1 枚の請求書を、計算 → 文書 → 保存 → 記録 の順で、前の段が済んでから次の段へ進めて発行したいとき（issue）。",
        "税の事実を G-07 の computeInvoiceTax で出させ、need_input や記載事項の欠けがあれば、文書を作らずにそのまま need_input・missing に添えて受け取りたいとき。invoice・tax_table・taxpayer_profile は G-07 の入力そのままです。",
        "PDF を、請求書番号を冪等キー（invoice/<番号>）にして、C-05 の継手で 1 回だけ保存したいとき。結果が APPLIED か DEDUP_HIT のときだけ保存済みです。",
        "発行の事実を、H-01 の継手で追記専用（append-only）の台帳に AuditEventEnvelope として記録したいとき。台帳へ渡すのは digest だけで、請求書の中身は渡しません。",
        "結果の分からなかった段を、同じ番号・同じ中身の issue で、同じ要求・同じ封筒のまま送り直したいとき。",
        "請求書番号ごとの段と digest を確かめたいとき（status）。"
      ],
      "do_not_use_when": [
        "発行できたこと（ISSUED）を、請求の承認・相手への送付・債権の成立・記載事項の法的な十分さとして使いたいとき。",
        "発行できたことを、税額が税務上正しいことを示すものとして使いたいとき。発行できたことは税務上正しいことを示すものではありません。税の事実は G-07 のままで、事実の正本は税率の表と事業者の選択です。",
        "請求書番号の採番・一意性・連番を任せたいとき。呼び出し側の役目です。",
        "相手への送付・受領、入金・債権の管理まで任せたいとき。外部の電子請求の送付・受領の継手は、この部品に入りません。",
        "PDF のレイアウトや、記載事項が PDF に実際に載っていることを確かめたいとき。G-07 の記載事項の検めは、入力の申告に対するものです。",
        "再起動をまたいで途中の段を続けたいとき。状態はこのプロセスの中だけで、再起動で途中の段と保った要求は消えます（保存は冪等キーで二重になりませんが、台帳の封筒は作り直されます）。",
        "本物の文書生成・保存・台帳の backend で確かめた部品が要るとき。本物の backend には一度も接続していません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき（欄の過不足・請求書番号の形など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "G-07 が ok: true を返さないとき TAX_INPUT_INVALID（tax_errors を添える）、G-07 の関数が投げたとき PORT_FAILED、事実の形が違う・inference か advice が null でないとき TAX_FACTS_INVALID で、文書を作りません。",
        "G-07 の事実に need_input があるとき NEEDS_INPUT（推測で埋めない）、記載事項の欠けがあるとき REQUIRED_ITEMS_MISSING（直し方は言わない）で、書類を作る前に止め、保存も記録もしません。",
        "同じ請求書番号で別の中身（請求書・税率の表・事業者の選択のどれか）のとき INVOICE_NUMBER_REUSED で止めます。発行済みの番号へのもう一度の issue は already_issued: true を返します。",
        "文書の継手の結果が分からないとき RENDER_UNKNOWN、送らなかったとき RENDER_REFUSED、変換できなかった・部分だけ・停止・空の PDF のとき RENDER_FAILED で、保存へ進みません。",
        "保存の継手の結果が分からないとき STORE_UNKNOWN（同じ要求を保つ）、送らなかったとき STORE_REFUSED（同じ冪等キーで別の中身を含む）、停止した・結果が APPLIED・DEDUP_HIT でないとき STORE_FAILED で、記録へ進みません。",
        "台帳の継手の結果が分からないとき（state() が投げた場合を含む）LEDGER_UNKNOWN（同じ封筒を保つ）、台帳が断った・先頭が進んで封筒がつながらない・先頭の形が不正なとき LEDGER_REFUSED で、発行済みにしません。",
        "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。"
      ],
      "what_it_does_not_do": [
        "請求書生成の部品は継手ではありません。自分の Request/Receipt は無く、文書の変換・保存・台帳への追記という外への効果は、束ねた J-01・C-05・H-01 の継手がそれぞれの契約で起こします。この部品自身は外へ触れません。",
        "束ねた部品の意味（事実だけ・変換できた ≠ 中身の確認・保存できた ≠ 受け入れ・記録できた ≠ 承認）は、それぞれの部品の側に残ります。",
        "発行できたことは、請求の承認・債権の成立・記載事項の法的な十分さではありません（approval: false・claim: NOT_DERIVED・legal_sufficiency: NOT_VERIFIED、derives_authority: false）。",
        "発行できたことは、税務上正しいことを示すものではありません。税務の判断はせず、事実だけを返し、直し方を返しません（税務相談は税理士の独占業務です）。",
        "請求書番号の採番・一意性・連番、相手への送付・受領、請求の承認、入金・債権の管理はしません。",
        "本物の文書生成・保存・台帳の backend には一度も接続していません。単体試験とファズは代役で、本物の 4 部品のコードとの結線は、各継手の試験用の代役を相手に確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の請求書ソフトの使い方の雛形ではありません。"
      ],
      "integration": "src/invoice.js の createInvoiceIssuance(options) で作ります。受け取るのは参照・store_target・ledger・ports・任意の timestamp で、{ ok, errors, instance } を返します。issue と status の結果には approval: false が付き、段に進んだ請求書は stage と digest を添えます。利用側が用意する port は taxFacts（G-07）・render.transform（J-01）・store.submit（C-05）・ledger.append と state（H-01）です。副作用は束ねた継手が起こし（EXTERNAL_IRREVERSIBLE）、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-05",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "サブスク・継続課金 — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、定期課金の要求（SubscriptionBillingRequest）の請求（BILL_CYCLE）・一時停止（PAUSE）・再開（RESUME）・解約（CANCEL）を、pin した一覧と、お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（SubscriptionBillingReceipt）を照合する継手（つぎて）です。外れれば送らず、受理しません（fail-closed）。",
      "problem": "定期課金を外の決済基盤へ運ぶとき、許していない請求や再開を送ってしまうこと、同じ請求を二度送ってしまうこと、op と合わない状態の receipt やお金が動いたか分からない receipt を信じてしまうことを防ぎます。",
      "use_when": [
        "定期課金の請求（BILL_CYCLE→BILLED）・一時停止（PAUSE→PAUSED）・再開（RESUME→RESUMED）・解約（CANCEL→CANCELLED）を外の決済基盤へ運び、receipt が op に合う状態かを照合したいとき。",
        "お金が動く操作（請求・再開）を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
        "請求に冪等キーを必須にし、同じキーの請求を重複（DEDUP_HIT、新しい効果なし）として扱いたいとき。",
        "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
      ],
      "do_not_use_when": [
        "請求できたことを、業務の承認として扱いたいとき。定期課金の成功は業務の承認ではありません（business_approval は運びから導きません）。",
        "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
        "冪等キーを検めます。冪等キーの無い請求（BILL_CYCLE）、同じキーで別の中身の要求は送りません。",
        "重複なのに効果を名乗る receipt は受理しません。重複（DEDUP_HIT）の効果は NONE です。",
        "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
        "業務の承認を名乗る receipt は受理しません。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "定期課金の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
        "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に SubscriptionBillingRequest を渡すと、DONE（result に billing_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 定期課金の操作は EXTERNAL_IRREVERSIBLE、重複・辞退・失敗などは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-06",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "支払・送金（payout） — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、支払い（payout）の要求（PayoutRequest）を、人・組織から受け取った根拠の参照・pin した支払い先・お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（PayoutReceipt）を契約どおりに照合する継手（つぎて）です。ERABAZU は支払いの許可を作らず、根拠が無ければ送りません（fail-closed）。",
      "problem": "事業者への支払いを外の決済基盤へ運ぶとき、人・組織の根拠が無いまま送ってしまうこと、pin していない支払い先へ送ってしまうこと、支払いの許可を名乗る receipt を信じてしまうことを防ぎます。支払えることを支払いの許可と取り違えないための検めです。",
      "use_when": [
        "事業者への支払いを、人・組織が出した根拠を recordAuthorityEvidence(evidence_ref, origin) で受け取った後だけ運びたいとき（origin は HUMAN か ORGANIZATION だけ。この操作はチャージバックの受け入れの根拠も受け取ります）。",
        "支払い先を、pin した一覧（payout_destinations）に限りたいとき。",
        "支払いを、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
        "同じ支払いを二度動かしたくないとき。冪等キーを必須にし、同じ request_id は二度送らず、障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "この継手に支払いの許可を出させたいとき。ERABAZU は支払いの許可を作りません。支払いの実行は人の役目です（Kernel と同じ承認の境界）。",
        "根拠が本物か、額・通貨・税・手数料が正しいかを確かめたいとき。この継手はどちらも確かめません（外の判断と人・組織の役目です）。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "根拠を検めます。根拠の参照が無い支払い、recordAuthorityEvidence で受け取っていない根拠を使う支払い、origin が ERABAZU の根拠は送りません。",
        "支払いの許可を名乗る receipt は受理しません。",
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
        "pin した一覧を検めます。pin していない支払い先、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "冪等キーを検めます。冪等キーの無い支払い、同じキーで別の中身の要求は送りません。",
        "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "支払えたことは支払いの許可ではありません。result の payout_authorization も運びから導かず、どの結果にも approval: false が付きます。",
        "支払いの許可の中身（本物の根拠か）、額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で根拠を受けた後、submit(request) に PayoutRequest を渡すと、DONE（result に payout_state・payout_authorization）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-07",
      "category": "G/決済・資金",
      "kind": "Kernel",
      "title": "税計算 — 税計算の部品（Kernel）",
      "summary": "税計算の部品（Kernel）は、日本の消費税（標準税率 10%・軽減税率 8%・旧税率 8%）について、適格請求書（または適格簡易請求書）1 枚の明細から、税率ごとの対価の合計と消費税額等、記載事項の欠けを事実として返す単独の部品です。税率はコードに持たず、渡された税率の表（期間と国・地方の内訳つき）から選び、端数処理の方法は事業者の設定から請求書の日付で選びます。表や設定で決まらないものは推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "「税込」の裏で、税額を明細ごとに丸めてしまうこと、浮動小数点の誤差が税額に入ること、コードに持った税率で計算してしまうこと、表や設定で決まらない入力を推測で埋めてしまうことを防ぎます。税額の端数処理は、1 枚につき税率ごとに 1 回だけです。",
      "use_when": [
        "適格請求書（QUALIFIED）か適格簡易請求書（SIMPLIFIED）1 枚の明細（1〜1000 件）から、税率ごとの対価の合計と消費税額等を事実として出したいとき。",
        "税額の端数処理を、1 枚につき税率（率の内訳 rate・national_rate・local_rate の組）ごとに 1 回だけ、事業者の設定の方法（FLOOR 切捨て・CEIL 切上げ・HALF_UP 四捨五入）で行いたいとき。明細ごと・表の行ごとには丸めません。",
        "税抜（TAX_EXCLUDED）か税込（TAX_INCLUDED）か（1 枚に 1 つ）に応じて、税額を浮動小数点を使わず正確な分数で出したいとき。金額と率は 10 進の文字列で渡します。",
        "税率を、使う側が渡す税率の表（行ごとの期間・国と地方の内訳・出典つき）から選びたいとき。同梱の表は bundledTaxTable() が写しを返し、ほかの表を tax_table として渡すこともできます。",
        "記載事項の欠け（氏名又は名称・登録番号・取引年月日・取引の内容・軽減税率の対象である旨など）を、計算と同じ呼び出しで一覧にしたいとき。欠けがあっても計算は止めません。",
        "同じ請求書から、キーの並びに関係なく同じ invoice_digest（RFC 8785 の正準 JSON の sha256）と、byte 単位で同じ出力が要るとき。時計・乱数・環境の locale を使いません。"
      ],
      "do_not_use_when": [
        "期間の集計（売上税額・仕入税額の積上げ計算、割戻し計算との選択）、簡易課税のみなし仕入率、2割特例、輸入消費税が要るとき。この部品は請求書 1 枚の中で閉じる計算だけです。",
        "非課税・不課税・免税の判定、源泉徴収、ほかの国の税が要るとき。カタログの一言「消費税・源泉等」のうち、源泉徴収は含みません。",
        "品目が軽減税率の対象かどうか、経過措置が当たるかどうかを判定させたいとき。呼び出し側が rate_class と rate_id の名指しで決めて渡します。",
        "登録番号が国税庁の公表サイトの登録簿にあるかを確かめたいとき。T と 13 桁の数字の形だけを検め、登録簿には照会しません。",
        "負の金額（値引き・返品・対価の返還等）を含む請求書を扱うとき。v1 は負の金額を受け取らず、NEGATIVE_AMOUNT で止めます。",
        "ほかの国の通貨の金額を扱うとき。UNSUPPORTED_CURRENCY で止めます。",
        "税額の国・地方への分け方が要るとき。国と地方の内訳は率だけを出します。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が { invoice, tax_table, taxpayer_profile } の 3 つだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキーは受け取らず、素のデータに写してから検めます。",
        "請求書の形が違うとき（知らないキー・足りないキー・語彙の外の値・単独のサロゲート・明細が 0 件か 1000 件を超える・reduced_marker が true 以外）INVALID_INVOICE、日付が実在する暦の日でないとき INVALID_DATE、同じ line_id の明細が 2 つあるとき DUPLICATE_LINE_ID で止めます。",
        "金額が { value, scale, currency } の決まった形でないとき（scale が 0〜6 の整数でない・小数の桁数が scale と違う）MALFORMED_AMOUNT、金額が負のとき NEGATIVE_AMOUNT、通貨が違うとき UNSUPPORTED_CURRENCY で止めます。",
        "税率の表の形が違うとき（出典の url が https でない場合を含む）INVALID_TAX_TABLE、行に出典が無いとき MISSING_SOURCE、率が決まった形でない・0 のとき MALFORMED_RATE、national_rate + local_rate が rate と等しくないとき RATE_SPLIT_MISMATCH で止めます。",
        "期間の終わりが始まりより前のとき INVALID_PERIOD、同じ rate_id の行が 2 つあるとき DUPLICATE_RATE_ID、同じ区分の行の期間が重なるとき OVERLAPPING_RATE_ROWS、事業者の設定の形が違うとき（端数処理の方法が FLOOR・CEIL・HALF_UP 以外など）INVALID_PROFILE で止めます。",
        "取引の日が無いとき、取引の日に当たる行が表に無いとき、名指しの rate_id が表に無い・区分が違う・日が合わないとき、請求書の日付に当たる端数処理の設定が 0 件か 2 件以上のときは、推測せず need_input にし、groups を空、invoice_totals を null にします（途中までの計算を出しません）。",
        "記載事項（消法57の4①②、インボイスQ&A 問54・問58）のうち、氏名又は名称・登録番号（形だけ）・取引年月日・取引の内容と軽減税率の対象である旨・交付を受ける事業者の名称（適格請求書だけ）を入力の欄から、税率ごとの合計・適用税率・税額を載せるかを presentation の申告から検め、無いものを required_items の missing に並べます。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは税の事実だけです。inference と advice はいつも null で、出力のどの文字列にも直し方や判断の語を入れません。approval は false、authority は NOT_DERIVED です（derives_authority: false）。",
        "結果は事実の提示であって、税務上の判断ではなく、申告が正しいことを示すものでもなく、税務の判断や申告の代行でもありません。税務相談は税理士の独占業務です。経過措置の当否・品目の区分・登録番号が登録簿にあるかは、この部品の外で確かめます。",
        "期間の集計（積上げ計算・割戻し計算の選択の当否）、簡易課税、2割特例、輸入消費税、非課税・不課税・免税の判定、源泉徴収、ほかの国の税は扱いません。",
        "標準税率の明細に reduced_marker が付いていることは検めません（記載事項の欠けとしても出しません）。sales_tax_method・taxation_method は形を検めて写すだけで、計算には使いません。",
        "税込で通貨の最小単位未満を含む金額を渡し、切上げ・四捨五入で税額が合計を超えると、税抜の合計は負の値になります。そのまま事実として出し、直しません。",
        "同梱の表は 2026-10-01 時点の国税庁の資料による 3 行だけです。2014-04-01 より前の税率と、成立していない改正案は入っていません。表に行の無い期間の取引の日は need_input になります。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/tax-facts.js の computeInvoiceTax({ invoice, tax_table, taxpayer_profile }) を呼びます（例外を投げません）。受け取るのは請求書（明細 lines・presentation など）、税率の表（行ごとの率の内訳・期間・出典）、事業者の設定（rounding）です。返すのは { ok: true, errors: [], facts }（groups・invoice_totals・required_items・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, where }] }（where は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-08",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "為替レートの取得 — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、為替レートの要求（FxRateRequest）を、pin した通貨の組（fx_pairs）とレートの出どころ（rate_sources）で検めてから運び、戻った receipt（FxRateReceipt）を契約どおりに照合する継手（つぎて）です。観測したレートは権威ではなく、権威を名乗る receipt は受理しません（fail-closed）。",
      "problem": "為替レートを外から取ってくるとき、pin していない通貨の組や出どころのレートを使ってしまうこと、観測したレートを権威として扱ってしまうことを防ぎます。",
      "use_when": [
        "pin した通貨の組と出どころに限って為替レートを観測して運び、結果（rate_state・rate_ref）として受け取りたいとき。",
        "受け取ったレートを、権威ではない観測値として扱う前提で使いたいとき（rate_authority は運びから導きません）。"
      ],
      "do_not_use_when": [
        "通貨の変換（換算）をしたいとき。この継手は為替レートの取得（観測したレートの運搬）の継手で、換算の機能は含みません。",
        "観測したレートを、権威のあるレートとして扱いたいとき。観測したレートは権威ではありません。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin した一覧を検めます。pin していない通貨の組、pin していないレートの出どころ、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "権威を名乗るレートの receipt は受理しません。",
        "外への効果を増やす receipt は受理しません。為替レートの要求の効果は NONE です。",
        "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "為替レートの要求を運ぶ継手で、換算（通貨の変換）の機能は含みません。",
        "観測したレートは権威ではありません。どの結果にも approval: false が付きます。",
        "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に FxRateRequest を渡すと、DONE（result に rate_state・rate_ref・rate_authority）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-09",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "突合用の明細取り込み（processor） — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、明細の取り込みの要求（StatementIntakeRequest）を、pin した明細の出し手（processors）で検めてから運び、戻った receipt（StatementIntakeReceipt）を契約どおりに照合する継手（つぎて）です。明細を運んでも突き合わせではなく、突き合わせを名乗る receipt は受理しません（fail-closed）。",
      "problem": "決済の明細を外から取り込むとき、pin していない出し手の明細を取り込んでしまうこと、明細を運んだことを明細と入金の突き合わせが済んだことと取り違えることを防ぎます。",
      "use_when": [
        "pin した明細の出し手に限って明細を取り込んで結果（intake_state）として受け取り、突き合わせは核（reconcile-facts）や人・組織に任せたいとき。",
        "取り込みと突き合わせを分けて扱いたいとき。突き合わせを名乗る receipt は受理せず、reconciliation は運びから導きません。"
      ],
      "do_not_use_when": [
        "明細と入金の突き合わせをしたいとき。この継手は突き合わせをしません。明細の突き合わせは外の判断と人・組織の役目です。",
        "明細の額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin した一覧を検めます。pin していない明細の出し手、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
        "突き合わせを名乗る receipt は受理しません。",
        "外への効果を増やす receipt は受理しません。明細の取り込みの効果は NONE か EXTERNAL_REVERSIBLE（FAILED は NONE）です。",
        "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "明細の突き合わせはしません。明細を運んでも突き合わせではなく、result の reconciliation も運びから導きません。",
        "取り込めたことは承認ではありません。どの結果にも approval: false が付きます。",
        "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に StatementIntakeRequest を渡すと、DONE（result に intake_state・reconciliation）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: NONE か EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-10",
      "category": "G/決済・資金",
      "kind": "Tsugite",
      "title": "チャージバックの申し立て — 決済の運び継手（Tsugite）",
      "summary": "決済の運び継手（Tsugite）は、チャージバックの要求（DisputeRequest）の申し立て（OPEN）・証拠の提出（SUBMIT_EVIDENCE）・観測（OBSERVE）・受け入れ（ACCEPT）を検めてから運び、戻った receipt（DisputeReceipt）が op どおりの状態かを照合する継手（つぎて）です。受け入れは、人・組織の根拠を受け取った後で、お金が動く操作として設定で許したときだけ送ります。運んでも解決の権限ではありません（fail-closed）。",
      "problem": "チャージバックを外の決済基盤へ運ぶとき、証拠の参照の無い提出を送ってしまうこと、人・組織の根拠の無いまま受け入れ（ACCEPT）を送ってしまうこと、解決の権限を名乗る receipt を信じてしまうことを防ぎます。受け入れは取り消せない業務の判断なので、根拠があるときだけ運びます。",
      "use_when": [
        "チャージバックの申し立て（OPEN→OPENED）・証拠の提出（SUBMIT_EVIDENCE→EVIDENCE_SUBMITTED）・観測（OBSERVE→OBSERVED）・受け入れ（ACCEPT→ACCEPTED）を運び、receipt が op に合う状態かを照合したいとき。",
        "チャージバックの解決を人・組織の判断に残し、運びからは解決の権限（resolution_authority）を導かないようにしたいとき。",
        "チャージバックの受け入れ（ACCEPT）を、人・組織が出した根拠を recordAuthorityEvidence で受け取った後だけ、設定 allow_money_movement: true で許したときだけ運びたいとき。"
      ],
      "do_not_use_when": [
        "人・組織の根拠が無いまま、または設定 allow_money_movement が true でないまま、チャージバックを受け入れたい（ACCEPT）とき。この継手は送りません（REFUSED）。",
        "チャージバックを解決したいとき、または返金をしたいとき。この継手はチャージバックの申し立て・証拠の提出・観測・受け入れを運ぶ継手で、解決の権限は無く、返金の機能は含みません。受け入れを運んでも解決ではありません。",
        "受け入れの根拠が本物の判断かを確かめたいとき。この継手は、人・組織から受け取った参照かだけを見ます。",
        "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "受け入れの根拠を検めます。根拠の参照が無い受け入れ（ACCEPT）、recordAuthorityEvidence で受け取っていない根拠を使う受け入れ、origin が ERABAZU の根拠の受け入れは送りません。受け入れ以外の op に根拠の欄を付けた要求も送りません。",
        "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、受け入れ（ACCEPT）は送りません（REFUSED）。",
        "証拠の参照の無い証拠の提出は送りません。参照が空・パス・.. を含むものも送りません。",
        "解決の権限を名乗る receipt は受理しません。",
        "外への効果を増やす receipt は受理しません。効果は、観測は NONE、申し立て・証拠の提出は EXTERNAL_REVERSIBLE、受け入れは EXTERNAL_IRREVERSIBLE（FAILED は NONE）です。",
        "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
        "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
        "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
      ],
      "what_it_does_not_do": [
        "チャージバックの解決はしません。受け入れ（ACCEPT）を運んでも解決の権限ではなく、どの結果にも approval: false が付きます。解決は外の判断と人・組織の役目です。",
        "受け入れの根拠の中身（本物の判断か）は確かめません。人・組織から受け取った参照かだけを見ます。ERABAZU は争いを認める判断を作りません。",
        "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
        "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の決済サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の recordAuthorityEvidence で根拠を受けた後、submit(request) に DisputeRequest を渡すと、DONE（result に dispute_state・resolution_authority）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: port。副作用: 観測は NONE、申し立て・証拠の提出は EXTERNAL_REVERSIBLE、受け入れは EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-11",
      "category": "G/決済・資金",
      "kind": "Primitive",
      "title": "金額の 10 進表現 — 金額の 10 進表現（文字列＋scale）の部品（Primitive）",
      "summary": "金額の 10 進表現（文字列＋scale）の部品（Primitive）は、金額を { value, scale, currency }（10 進の整数の文字列・小数点の位置・通貨のコード）で持ち、10 進の文字列からの読み取り、末尾の 0 を削る正規化、同じ通貨の足し算・比較・等しさ、10 進の文字列への書き出しをする単独の部品です。計算は BigInt だけで、浮動小数点を使わず、丸めません。",
      "problem": "小数の誤差で、金額が端数の分だけずれる事故を防ぎます。浮動小数点を使わないので誤差が入らず、38 桁に収まらない結果は黙って丸めずに止めます。受け取れない値は、決まったコードと位置で止めます。",
      "use_when": [
        "10 進の文字列（-?[0-9]+(.[0-9]+)?、ASCII の数字だけ）を、通貨のコードとあわせて金額に読みたいとき（parseDecimal）。scale は書かれた小数の桁数のままで、正規化しません。",
        "末尾の 0 を削り、scale を最小にした金額にそろえたいとき（normalize。0 は { value: \"0\", scale: 0 }）。",
        "同じ通貨の 2 つの金額を、丸めずに足したいとき（add。結果は正規化した金額）。",
        "scale によらず、数の大小（compare の -1・0・1）や等しさ（equal）を知りたいとき。equal は通貨が違えば false で、エラーではありません。",
        "金額を、小数の桁数がちょうど scale の 10 進の文字列に書き出したいとき（toText）。parseDecimal(toText(a)) は同じ value・scale に戻ります。",
        "時計・乱数・環境の locale・外の接続を持たない、決定的な処理として手元で呼びたいとき。"
      ],
      "do_not_use_when": [
        "丸め・掛け算・割り算・為替の換算が要るとき。この部品は扱いません。",
        "記号や桁区切りの付いた表示用の文字列が要るとき。表示用の整形は L-03 の部品の役目です。",
        "通貨のコードが ISO 4217 の表にあるか、通貨ごとの小数の桁数に合うかを確かめたいとき。この部品は形（英大文字 3 字）だけを検めます。",
        "38 桁・scale 18 を超える金額を扱うとき。表せないので止めます（TOO_MANY_DIGITS・INVALID_SCALE）。",
        "整数部の先頭の 0 を断りたいとき。parseDecimal は先頭の 0 を受け取って捨てます。より狭い形が要るときは、使う側で検めます。",
        "text の長さの上限が要るとき。上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が素のオブジェクトでない、決めたキーの外を持つ、getter・列挙しないプロパティ・symbol のキーを持つ、値を調べること自体が例外になるとき（Proxy の罠など）、INVALID_ARGUMENT で止めます。getter は呼びません。",
        "text・value が文字列でないとき（数として渡された値・NaN・Infinity・bigint・無いキー）、NOT_A_STRING で止めます。",
        "text が -?[0-9]+(.[0-9]+)? の形でないとき（指数・+・空白・桁区切り・\"1.\"・\".5\"・全角の数字・改行）、INVALID_DECIMAL_TEXT で止めます。",
        "value が -?[0-9]+ の形でない・先頭に余分な 0 があるとき INVALID_VALUE、\"-0\" などの負の 0 のとき NEGATIVE_ZERO で止めます。",
        "桁が 38 を超えるとき（足し算の結果を含む）TOO_MANY_DIGITS、scale が 0〜18 の整数でないとき（-0・小数・文字列）や小数の桁数が 18 を超えるとき INVALID_SCALE で止めます。",
        "currency が英大文字 3 字の文字列でないとき INVALID_CURRENCY、add・compare の 2 つの金額の通貨が違うとき CURRENCY_MISMATCH で止めます。",
        "止めたときは、決まった順で最初の 1 つだけを、コード（code）と位置（where。JSON Pointer で、2 つの金額の操作では /a・/b から始まる）で返します。"
      ],
      "what_it_does_not_do": [
        "丸め・掛け算・割り算・為替の換算・表示用の整形はしません。",
        "計算できたことは、金額が業務として正しいことでも、受け入れてよいことでもありません。",
        "通貨ごとの小数の桁数には照らしません。ISO 4217 の表に無いコードでも、形が合えば受け取ります。",
        "全角・ほかの文字体系の数字は受け取りません。数字は ASCII の 0〜9 だけです。",
        "計算の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の 10 進数のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/decimal-amount.js の parseDecimal({ text, currency })・normalize(amount)・add(a, b)・compare(a, b)・equal(a, b)・toText(amount) を呼びます（どれも投げません）。金額は { value, scale, currency }（value は符号を除いて 38 桁までの整数の文字列、scale は 0〜18 の整数、currency は英大文字 3 字）です。返すのは { ok: true, errors: [], amount | result | equal | text } か { ok: false, errors: [{ code, where }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-12",
      "category": "G/決済・資金",
      "kind": "Kernel",
      "title": "取引の事象 ID — 取引の事象 ID（txn_id）の部品（Kernel）",
      "summary": "取引の事象 ID（txn_id）の部品（Kernel）は、取引・証憑の事実（kind・source・occurred_on・amount・counterparty）から、決まった規則で事象の ID（txn_id）を出す単独の部品です。ハッシュする形を RFC 8785 の正準 JSON にして SHA-256 をとり、先頭 16 bytes を小文字の hex 32 文字にします。必要な事実が無いときは推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "二重計上を防ぐ照合の鍵には、同じ事実からいつも同じ値が要ります。金額の末尾の 0 と scale の書き方の違い、入力のキーの順、浮動小数点の誤差、raw_refs のような出どころの参照によって、同じ事実の ID が変わってしまうことを防ぎます。",
      "use_when": [
        "取引・証憑の事実から、二重計上を防ぐ照合の鍵として、同じ事実から同じ値になる事象の ID（txn_id）を作りたいとき。",
        "同じ値の金額を、scale の書き方によらず同じ ID にしたいとき。金額は BigInt で末尾の 0 を削って scale を最小にし、浮動小数点を使いません。通貨・符号・値が違えば別の ID です。",
        "取引先を、出どころの ID（id_in_source）か名前で表したいとき。id_in_source があって空でなければそれだけを使い、無ければ名前を NFKC にして前後の空白を削ったものを使います。",
        "必要な事実が欠けているときに、推測で埋めず、無い事実の位置（JSON Pointer）の一覧 missing を受け取りたいとき。state は need_input で、エラーではありません。",
        "ID と一緒に、ハッシュした形（hashed）と、その正準 JSON の文字列（canonical）も受け取り、ID の元を確かめたいとき。",
        "時計・乱数・環境の locale を使わず、入力のキーの順によらない、決定的な出力が要るとき。外の境界・状態はありません。"
      ],
      "do_not_use_when": [
        "2 つの記録が同じ取引かどうかを判断させたいとき。同じ欄の値を持つ別々の取引（同じ日・同じ金額・同じ取引先の 2 回の買い物など）は同じ txn_id になります。区別は source_key の役目で、G-13・G-14 の部品が扱います。",
        "表記のゆれを寄せたいとき。大文字と小文字、(株) と 株式会社、ゼロ幅の文字、空白の数の違いは別の ID になります。別名の候補を出すことは、この部品に含みません。",
        "kind（evidence か transaction か）・source・id_in_source を決めさせたいとき。どれも呼び出し側が決めて渡します。kind は申告されたものを使い、判定しません。",
        "記録の形の検め・supersedes の扱い・重複のまとまり分けが要るとき。記録の形と supersedes は G-13、同一性の基準は G-14 の部品の範囲です。",
        "ID が衝突しないことを前提にしたいとき。txn_id は 128 bit に切り詰めたハッシュで、衝突しないことは約束しません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は Node.js（ICU）の String.prototype.normalize によるため、Unicode の版が変わると結果が変わりえます。"
      ],
      "what_it_checks": [
        "引数が 6 つのキー（kind・source・occurred_on・amount・counterparty・raw_refs）だけを持つ素のオブジェクトでないとき、getter・列挙しないプロパティ・symbol のキーを持つとき（raw_refs を除く）、値を調べること自体が例外になるとき（Proxy の罠など）は INVALID_ARGUMENT で止めます。",
        "kind が evidence・transaction でないとき INVALID_KIND、source が ^[a-z0-9][a-z0-9_-]{0,62}$ の文字列でないとき INVALID_SOURCE、occurred_on が YYYY-MM-DD の実在する暦の日（0001〜9999 年）でないとき INVALID_DATE で止めます。",
        "amount が { value, scale, currency } だけを持つ素のオブジェクトでないとき、value が -?[0-9]+ の形でない（先頭の 0・\"-0\"・符号を除いて 38 桁を超える）とき、scale が 0〜18 の整数でない（-0 を含む）とき、currency が英大文字 3 字でないときは INVALID_AMOUNT で止めます。",
        "counterparty が { id_in_source, name } のうちだけを持つ素のオブジェクトでないとき、どちらかが文字列か null でないとき、単独のサロゲートを含むときは INVALID_COUNTERPARTY で止めます。id_in_source があるときも、名前の形は検めます。",
        "形の違う欄が 1 つでもあれば、無い欄があってもエラー（{ ok: false }）にし、最初に見つけた 1 つだけを返します。検める順は 引数の形 → kind → source → occurred_on → amount → counterparty です。",
        "raw_refs は読まず（形も検めず、getter も呼ばず）、ID に入れません。検めた値とハッシュする値は同じです。",
        "結果を、src の補助関数も src/canonical.js も crypto も使わずに規則から書き直した独立オラクル（SHA-256 も純 JS）と突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC だけはオラクルも同じ関数を使います。",
        "15 件の既知の答え（test/known-answers.json）で試験しています。正準の文字列は規則から手で書き、txn_id は src を使わない純 JS の SHA-256 で求めたものです。G-13 の部品と同じ答えを共有します。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、出力のどの文字列にも判断の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "事象 ID は決まった欄から決まった規則で作った値で、記帳や突合の判断ではありません。2 つの記録が同じ取引かどうかは決めません。",
        "id_in_source は正規化しません（前後の空白も含めてそのまま使います）。名前は NFKC と前後の空白の削りだけで、それ以上は寄せません。",
        "通貨のコードは形（英大文字 3 字）だけを検め、ISO 4217 の表とは照らしません。",
        "id_in_source と name の長さの上限は持ちません。NFKC・正準化・ハッシュには長さに比例する時間がかかります。大きさの上限は I-06 の部品か使う側で決めます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/txn-id.js の computeTxnId(input) を呼びます（例外を投げません）。受け取るのは { kind, source, occurred_on, amount: { value, scale, currency }, counterparty: { id_in_source?, name? }, raw_refs? } です。返すのは { ok: true, errors: [], facts }（state・txn_id・hashed・canonical・missing、inference: null・advice: null）か { ok: false, errors: [{ code, where }] }（where は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-13",
      "category": "G/決済・資金",
      "kind": "Kernel",
      "title": "取引・突合の記録の検め — 共通形の取引・突合の記録の検めの部品（Kernel）",
      "summary": "共通形の取引・突合の記録の検めの部品（Kernel）は、共通形の取引（erabazu.common.txn/1）の欄をすべて検め、txn_id が G-12 の事象 ID の規則で計算し直した値と同じかを確かめる単独の部品です。突合（erabazu.common.match/1）は取引とは別の記録として検めます。取引の集合について supersedes の鎖の事実と古い順の鎖を出しますが、どの版が正しいかは決めません。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "帳簿の元になる記録の形は、誰も見ないまま使われがちです。記録に書かれた txn_id を言い分のまま信じること、突合の情報を取引の記録に書き込んでしまうこと、supersedes の鎖の切れ・循環・枝分かれに気づかないことを防ぎます。",
      "use_when": [
        "共通形の取引 1 件を、閉じた形（schema・txn_id・kind・occurred_on・amount・counterparty・lines・source・raw_refs・probe_version・extracted_at と、任意の supersedes・source_key）として検めたいとき（checkTxn）。",
        "記録の txn_id が、kind・source・occurred_on・amount・counterparty から G-12 の規則で計算した値と同じかを確かめたいとき。違えば TXN_ID_MISMATCH と、計算し直した値 recomputed_txn_id を返します。",
        "突合の記録（evidence_txn・transaction_txn・basis・matcher_version・confidence）を、取引とは別の記録として検めたいとき（checkMatch）。取引の欄を持つ突合の記録は受け取りません。",
        "取引の集合（10,000 件まで）について、supersedes の鎖を古い順に並べ、相手が無い・同じ id・別の source・循環・枝分かれを problems として受け取りたいとき（checkSupersedesChain）。",
        "時計・乱数・環境の locale を使わない、決定的な出力が要るとき。checkSupersedesChain の結果は入力の並びにもよりません。外の境界・状態はありません。"
      ],
      "do_not_use_when": [
        "どの版が正しいか・どれを使うかを決めさせたいとき。supersedes の鎖の事実を並べるだけです。",
        "評価（為替の換算など）や、重複のまとまり分けが要るとき。まとまり分けは G-14 の部品の範囲です。",
        "突合の evidence_txn・transaction_txn が実在する取引か、それぞれの kind が合っているかを確かめたいとき。id の形と 2 つが違うことだけを検めます。",
        "明細の金額の合計が取引の金額と合うか、明細の通貨が取引の通貨と同じかを確かめたいとき。どちらも検めません。",
        "欠けた事実を集めたいとき。記録の欄が足りなければ need_input ではなく INVALID_RECORD で止めます。この部品は保存された記録の検めで、無い事実の一覧を出す G-12 とは役割が違います。",
        "中身を直しても txn_id が変わらない訂正を、同じ集合で扱いたいとき。同じ txn_id が 2 件ある集合は DUPLICATE_TXN_ID で止まります。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は実行環境の ICU によるため、Unicode の版が変わると名前の正規化の結果が変わることがあります。"
      ],
      "what_it_checks": [
        "記録が素のデータでないとき（getter・クラスのインスタンス・穴のある配列・symbol のキー・__proto__ のキー・入れ子 8 段や配列 10,000 要素を超える）、知らないキー・足りないキーがあるとき INVALID_RECORD、値を調べること自体が例外になるとき INSPECTION_FAILED で止めます。",
        "schema が違うとき INVALID_SCHEMA、txn_id・supersedes などが小文字の hex 32 文字でないとき INVALID_TXN_ID、kind が語彙の外のとき INVALID_KIND、日付が実在する暦の日でないとき INVALID_DATE、金額の形が違うとき INVALID_AMOUNT で止めます。",
        "取引先が空（id_in_source が空で、NFKC と trim の後の name も空）か形が違うとき INVALID_COUNTERPARTY、明細・数量の形が違うとき INVALID_LINES、source の形が違うとき INVALID_SOURCE、raw_refs が 1〜1024 件の重複のない Ref の配列でないとき INVALID_RAW_REFS で止めます。",
        "probe_version の形が違うとき INVALID_PROBE_VERSION、extracted_at が RFC 3339 の日時でない（うるう秒・小文字の t と z を含む）とき INVALID_TIME、source_key の形が違うとき INVALID_SOURCE_KEY で止めます。検めは決まった順で、最初に当たった 1 つだけを返します。",
        "txn_id を記録の言い分のまま信じず、G-12 の規則で計算し直して比べます。違えば TXN_ID_MISMATCH で止めます。",
        "突合の記録が素のデータでない・知らないキー（取引の欄を含む）・足りないキーがあるとき、basis・matcher_version・confidence（exact・probable・manual）の形が違うとき INVALID_MATCH、2 つの id が同じとき SAME_TXN_MATCHED で止めます。",
        "鎖の引数が { txns } だけの素のオブジェクトでないとき INVALID_ARGUMENT、同じ txn_id が 2 件あるとき DUPLICATE_TXN_ID で止めます。問題のある辺（相手が無い・同じ id・別の source・枝分かれ・循環）は鎖につながず、SUPERSEDES_ で始まるコードの事実として problems に出します。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC と SHA-256 は、オラクルも Node.js の同じ関数を使います。"
      ],
      "what_it_does_not_do": [
        "返すのは記録の形と規則との一致の事実だけです。inference と advice はいつも null、approval は false で、権限も作りません（derives_authority: false）。入力の名前・説明の文字列はそのまま写します。",
        "結果は、取引の中身が正しいこと・どの版が正しいことの判断ではありません。会計の判断や記帳の代行でもありません。突合の当否・kind の当否・金額の評価（為替など）は、この部品の外です。",
        "kind（evidence か transaction か）は宣言をそのまま受け取り、判定しません。",
        "raw_refs が指す取得物が実在するか、probe_version が実在する版かは検めません（形だけ）。通貨の英大文字 3 字も形だけを検め、ISO 4217 の一覧とは照らしません。",
        "G-12 の部品そのものは読み込まず、規則の写しを中に持ちます。写しが同じことは、両方の既知の答えを試験で照らすことと、一度だけの突き合わせで確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/txn-record.js の checkTxn(record)・checkMatch(record)・checkSupersedesChain({ txns }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts } か { ok: false, errors: [{ code, where }] }（where は JSON Pointer。TXN_ID_MISMATCH は recomputed_txn_id も持つ）です。checkTxn の facts は txn_id・hashed・canonical・supersedes・source_key、鎖では chains と problems です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "G-14",
      "category": "G/決済・資金",
      "kind": "Kernel",
      "title": "取引の同一性の基準 — 取引の同一性の基準の部品（Kernel）",
      "summary": "取引の同一性の基準の部品（Kernel）は、呼び出し側が宣言した同一性の基準（source_key か、日付・金額・取引先から選んだ composite_key）に従って、取引の記録を同じ鍵のまとまりに分ける単独の部品です。宣言した鍵が無い記録は推測せず need_input に並べます。まとまりの中で欄が違えば differing: true にするだけで、統合も削除もしません。曖昧一致はしません。inference と advice はいつも null です。",
      "problem": "「同じ取引か」の判断の土台として、宣言した基準で同じ鍵になる記録をまとめます。鍵の無い記録を推測で埋めること、似た名前・近い日付・近い金額を同じものとして寄せてしまうこと、まとめるときに記録の違いを捨ててしまうことを防ぎます。",
      "use_when": [
        "記録にプラットフォームの取引 id（source_key）があり、それだけを鍵にまとめたいとき（basis: source_key）。identity_key は \"sk:\" と source_key をつないだ文字列です。",
        "id が無く、日付（occurred_on）・金額（amount）・取引先（counterparty）から 1〜3 個を選んで鍵にしたいとき（basis: composite_key）。鍵は選んだ欄だけの RFC 8785 の正準 JSON の SHA-256（小文字の hex 64 文字）です。",
        "金額の scale の違いや取引先の名前の全角と半角の違いを、G-12 の事象 ID と同じ正規化で同じ鍵にしたいとき。金額は末尾の 0 を削って scale を最小にし、取引先は id_in_source が空でなければそれだけ、無ければ名前を NFKC にして前後の空白を削ります。",
        "宣言した鍵が無い記録を、推測で鍵を作らず need_input として受け取りたいとき。各記録は groups か need_input のどちらかにちょうど 1 回入ります。",
        "同じ鍵のまとまりの中で記録の欄の有無や値が違うことを、differing として知りたいとき。書いたままの値で比べるので、scale・全角と半角のような表現の違いも違いとして数えます。",
        "時計・乱数・環境の locale を使わない、決定的な出力が要るとき。groups は identity_key の順、refs と need_input は入力の順です。"
      ],
      "do_not_use_when": [
        "似た名前・近い日付・近い金額の候補を出したいとき。曖昧一致はしません。取引先の名前は NFKC と前後の空白の除去だけで、それ以上は寄せません。",
        "記録の統合・削除や、どれを残すか（supersedes）を決めさせたいとき。まとまりを出すだけです。",
        "ほかの source の記録を混ぜて、source_key でまとめたいとき。記録に source の欄が無いので、source_key が偶然同じでも区別しません。呼び出し側が source ごとに渡します。",
        "宣言が composite_key のときに source_key も鍵に使いたいとき。宣言に従い、source_key は鍵に使いません（differing には数えます）。",
        "基準を部品に選ばせたいとき。どの基準を使うかは呼び出し側が宣言します。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は実行環境の ICU によるため、Unicode の版が変わると名前の正規化の結果が変わることがあります。"
      ],
      "what_it_checks": [
        "引数が { declaration, records } の 2 つのデータプロパティだけの素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキーは受け取りません。",
        "宣言の basis が source_key・composite_key でないとき、知らないキーがあるとき（source_key に fields を付けたものを含む）、fields が occurred_on・amount・counterparty から 1〜3 個の重なりのない配列でないとき INVALID_DECLARATION で止めます。",
        "records が素のデータの配列（10,000 件まで・入れ子 8 段まで）でないとき、記録に知らないキーがある・ref が無いか Ref の形でないとき INVALID_RECORDS、同じ ref の記録が 2 つあるとき DUPLICATE_REF で止めます。",
        "与えられた欄（source_key・occurred_on・amount・counterparty）の形が違うときは、宣言で使わない欄でも INVALID_FIELD で止めます。source_key の空文字も止めます。取引先が { } や空の文字列だけのときは、形の誤りではなく欄が無いもの（need_input）とします。",
        "検めは決まった順（引数 → declaration → 記録ごとの形 → ref の重なり）で、最初に当たった 1 つだけを返します。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC と SHA-256 は、オラクルも Node.js の同じ関数を使います。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null、approval は false で、権限も作りません（derives_authority: false）。入力の ref・source_key はそのまま写します。",
        "結果は、宣言した基準で同じ鍵になるかの事実で、同じ取引だという判断・どれを残すかの判断ではありません。重複の判断や記帳の代行でもありません。",
        "曖昧な重複の候補出し（suggest-dup）・別名の寄せ（suggest-alias）・統合・削除・supersedes の決定は含みません。",
        "通貨の英大文字 3 字は形だけを検め、ISO 4217 の一覧とは照らしません。",
        "G-12 の部品そのものは読み込まず、正規化の規則の写しを中に持ちます。写しは、Node.js の外の sha256sum で求めた既知の答え 4 件で確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/identity-rule.js の groupByIdentity({ declaration, records }) を呼びます（例外を投げません）。declaration は { basis: \"source_key\" | \"composite_key\", fields? }、records は { ref, source_key?, occurred_on?, amount?, counterparty? } の配列です。返すのは { ok: true, errors: [], facts }（groups・need_input など、inference: null・advice: null）か { ok: false, errors: [{ code, where }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-01",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "改変不能監査ログ — 監査台帳継手（Tsugite）",
      "summary": "監査台帳継手（Tsugite）は、検めた監査イベント封筒（AuditEventEnvelope）だけを追記専用（append-only）の backend へ送り、backend が返す受領（AuditAppendReceipt）を契約の式どおりに照合してから、台帳の先頭を進める継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "監査の記録を外部の追記専用の保管先へ送るとき、型の崩れた封筒や本文の混じった封筒を送ってしまうこと、そして保管先の受領が正しいかを確かめないまま「記録できた」と扱ってしまうことを防ぎます。照合できない追記を信頼しないための検めです。",
      "use_when": [
        "誰が何をしたかを、後から消せない形で残す監査ログを、外部の追記専用 backend に記録したいとき。",
        "backend から返る受領をそのまま信じず、契約の式（連鎖のハッシュ・通番・digest）で照合してから記録済みとしたいとき。",
        "同じイベントを二重に追記したくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
        "受領に不整合が出たら、人が確かめるまで追記を止めたいとき。"
      ],
      "do_not_use_when": [
        "監査イベントの本文そのものを保管したいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
        "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
        "追記の成功を、承認や決裁として扱いたいとき。追記の成功は承認ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。"
      ],
      "what_it_checks": [
        "封筒の型を検めます。追加のフィールド、決まった値（const）と違う値、genesis の規則に反するものは送りません。",
        "pin した台帳を検めます。pin していない台帳への追記は送りません。",
        "連鎖（previous_hash）と通番（sequence）を検めます。前のハッシュの改ざん、通番の巻き戻し・飛びがあれば、送らず、受理しません。",
        "digest を検めます。ハッシュは契約の式（RFC 8785 に制限した正準化と sha256。genesis の previous_hash_value は空文字）で計算し、式と違うものは受理しません。",
        "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません。",
        "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
        "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
        "本文が混じった封筒、大きすぎるイベント、種類を偽った封筒（content-type の偽り）、同じイベントの二重の追記を拒否します。"
      ],
      "what_it_does_not_do": [
        "改変できないかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
        "追記の成功は承認ではありません（approval: false）。",
        "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
        "透明性ログ（Merkle の包含証明）は扱いません。",
        "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "監査 backend の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと、APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false が付きます）。利用側が用意するもの: 追記専用 backend への port（append と、任意で lookup）。pin する endpoint と受領の形式、認証の SecretRef の解決も port 側。副作用は取り消せない外部への書き込み（EXTERNAL_IRREVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-02",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "アクセスログ — 監査台帳継手（Tsugite）",
      "summary": "監査台帳継手（Tsugite）は、「誰が何を見たか」の閲覧の記録を、監査の出来事（AuditEventEnvelope）として追記専用（append-only）の backend へ追記するときに使う継手（つぎて）です。検めた封筒だけを送り、返る受領（AuditAppendReceipt）を照合できたときだけ台帳の先頭を進めます。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "閲覧の記録を外部の追記専用の保管先へ積むとき、型の崩れた封筒や本文の混じった封筒を送ってしまうこと、同じ記録の二重の追記、通番の巻き戻り・飛び、そして保管先の受領が正しいかを確かめないまま「記録できた」と扱ってしまうことを防ぎます。",
      "use_when": [
        "閲覧の記録（誰が何を見たか）を、監査の出来事として、外部の追記専用 backend に 1 件ずつ追記したいとき。",
        "閲覧を捉えて AuditEventEnvelope にする側（利用側）を自分で用意でき、その封筒の追記と照合だけをこの継手に任せたいとき。",
        "backend から返る受領を、契約の式（連鎖のハッシュ・通番・digest）で照合してから、記録済みとしたいとき。",
        "同じ閲覧の記録を二重に追記したくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
        "受領に不整合が出たら、人が確かめるまで追記を止めたいとき。"
      ],
      "do_not_use_when": [
        "閲覧そのものを検知・収集したいとき。閲覧を捉えて出来事にするのは利用側の役目で、この継手は追記を運んで照合するだけです。",
        "閲覧した内容そのもの（本文）を台帳に積みたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
        "記録を書き換えたり削除したりしたいとき。この継手は追記だけを扱います。",
        "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "封筒の型を検めます。追加のフィールド、決まった値（const）と違う値、genesis の規則に反するもの、種類を偽った封筒（content-type の偽り）は送りません。",
        "pin した台帳を検めます。pin していない台帳への追記は送りません。",
        "連鎖（previous_hash）と通番（sequence）を検めます。前のハッシュの改ざん、通番の巻き戻し・飛びがあれば、送らず、受理しません。",
        "同じ event_id・同じ内容がもう追記済みなら backend に送らず ALREADY_APPENDED を返し、同じ event_id で内容が違うときは REFUSED を返します。",
        "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません。",
        "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
        "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
        "本文が混じった封筒と、大きさの上限（max_envelope_bytes）を超えるイベントを拒否します。"
      ],
      "what_it_does_not_do": [
        "閲覧を捉えることも、閲覧の出来事を作ることもしません。それは利用側の役目で、この継手は追記を運んで照合するだけです。",
        "追記の成功は承認ではありません（approval: false）。",
        "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
        "透明性ログ（Merkle の包含証明）は扱いません。backend の認証に使う秘密（SecretRef）の解決もしません（port 側の責務です）。",
        "H-02 専用の部品ではありません。H-01（改変不能監査ログ）・C-04（追記専用台帳）・H-08（改ざん検知チェーン）と同じ 1 つの部品が担い、試験と検査も共通です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に、閲覧の記録を表す AuditEventEnvelope を渡すと、APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 閲覧を封筒にする処理と、追記専用 backend への port（append、任意で lookup）。副作用は EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-03",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "同意記録ストア — 監査・プライバシー継手（Tsugite）",
      "summary": "監査・プライバシー継手（Tsugite）は、同意の取得・撤回の記録と問い合わせの要求（ConsentRecord）を、pin した目的と範囲で検めてから同意台帳の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。一つでも外れれば、送らず、受理しません（fail-closed）。同意台帳そのものは含みません。",
      "problem": "同意の取得・撤回を外部の同意台帳へ記録するとき、pin していない目的・範囲での記録、本人のメールアドレスや電話番号をそのまま送ってしまうこと、そして記録できたことを同意の権限（consent authority）と取り違えることを防ぎます。同意の記録は事実で、権限ではありません。",
      "use_when": [
        "同意の取得（RECORD_GRANT）と撤回（RECORD_WITHDRAWAL）の履歴を、外部の同意台帳の基盤に追記したいとき。",
        "記録した同意の状態を問い合わせ（QUERY）で確かめたいとき。問い合わせの副作用は NONE です。",
        "本人を仮名の参照だけで運び、メールアドレス・電話番号の形を基盤へ送りたくないとき。",
        "同意の目的と範囲を、pin した一覧（purpose_refs・scope_refs）に限りたいとき。空の一覧は何も許しません。",
        "基盤から返る receipt をそのまま信じず、request_id・request_digest・record_digest で照合してから記録済みとしたいとき。"
      ],
      "do_not_use_when": [
        "同意が有効かどうかを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
        "記録できたことを同意の権限として使いたいとき。consent_authority は NOT_DERIVED か人・組織に固定され、継手は導きません。",
        "記録した同意を消したり書き換えたりしたいとき。同意の記録は追記だけで、取り消せない外部への書き込み（EXTERNAL_IRREVERSIBLE）です。撤回は RECORD_WITHDRAWAL として記録します。",
        "すぐに使える同意台帳がほしいとき。実の基盤（同意台帳）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
        "個人情報を網羅的に見分けたいとき。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "H-03 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
      ],
      "what_it_checks": [
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "目的と範囲を検めます。pin していない目的（purpose）・範囲（scope）の要求は送りません。",
        "op と副作用の対応を検めます。QUERY は NONE、RECORD_GRANT・RECORD_WITHDRAWAL は EXTERNAL_IRREVERSIBLE で、対応しない要求・receipt は送らず、受理しません。",
        "receipt の状態と record_digest を検めます。op に答えていない状態（RECORD_GRANT なら GRANTED か UNKNOWN、RECORD_WITHDRAWAL なら WITHDRAWN か UNKNOWN 以外）、ほかの同意の record_digest（target が consent_ref と違う）は受理しません。",
        "同意の権限を導いたと名乗る receipt は受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "同意が有効か、同意の権限があるかは判断しません。同意の記録は事実で、権限ではありません。",
        "要る同意・足りない同意・承認経路の事実を扱うのは核（consent-facts。同意を与えません）、保持の事実を扱うのは核（policy-facts）の役目で、法的な権利は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の基盤（同意台帳）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
        "記録できたことは承認ではありません（approval: false）。",
        "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に ConsentRecord の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の purpose_refs・scope_refs。副作用は記録が EXTERNAL_IRREVERSIBLE、QUERY が NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-04",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "データ主体要求（DSAR）処理（運ぶだけ・判断しない） — 監査・プライバシー継手（Tsugite）",
      "summary": "監査・プライバシー継手（Tsugite）は、本人からの請求（DSAR）の要求を検めてから DSAR の窓口の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。DSAR は運ぶだけで、請求に応じるか・何を開示するか・削除するかは判断せず、実行もしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "DSAR を外部の DSAR の窓口へ運ぶとき、本人のメールアドレスや電話番号をそのまま送ってしまうこと、取り消せない効果や不明な効果を運搬の意図にしてしまうこと、そして運べたことを法的な権利（legal entitlement）の判断と取り違えることを防ぎます。",
      "use_when": [
        "DSAR の要求を、本人の仮名の参照だけで DSAR の窓口の基盤へ運びたいとき。",
        "運んだ結果を、request_id・request_digest・参照の一致で照合してから確かめたいとき。DONE の result には transport_state・dsar_type・legal_entitlement が添えられます。",
        "運搬の副作用を NONE〜EXTERNAL_REVERSIBLE の範囲にとどめたいとき。取り消せない効果・不明な効果は意図にできません。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "請求に応じる権利があるか、何を開示してよいかを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
        "開示・削除の請求に応じる窓口そのもの、あるいは開示・削除を実行する仕組みがほしいとき。この継手は DSAR の要求を基盤へ運ぶだけで、削除するかどうかは人・組織が決めます。実の基盤（DSAR の窓口）も含みません。",
        "運べたことを法的な権利の判断として使いたいとき。legal_entitlement は NOT_DERIVED か人・組織に固定され、継手は導きません。",
        "取り消せない効果を DSAR の運搬の意図にしたいとき。その要求は送りません。",
        "個人情報を網羅的に見分けたいとき。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "H-04 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
      ],
      "what_it_checks": [
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "DSAR の意図を検めます。取り消せない効果・不明な効果を意図にした要求は送りません。",
        "法的な権利を判断したと名乗る receipt は受理しません。",
        "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "法的な判断をしません。請求に応じる権利があるか、何を開示してよいかは、人・組織と核の役目です。要る同意・足りない同意の事実は核（consent-facts）が扱い、同意を与えません。",
        "開示・削除を決めず、実行もしません。DSAR は運ぶだけで、削除するかどうかは人・組織が決めます。DSAR の運搬は法的な権利の判断ではありません。",
        "実の基盤（DSAR の窓口）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
        "運べたことは承認ではありません（approval: false）。",
        "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に DSAR の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: transport_state・dsar_type・legal_entitlement。利用側が用意するもの: DSAR の窓口の基盤への port（submit、任意で lookup）。副作用は NONE〜EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-05",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "PII 検出・秘匿パイプライン — 監査・プライバシー継手（Tsugite）",
      "summary": "監査・プライバシー継手（Tsugite）は、黒塗り（redaction）の処理の要求（RedactionPipeline）を、pin した黒塗りの定義で検めてから黒塗りの処理の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。塗る処理そのもの、何を塗るかの決定、塗れているかの確認はしません。外れれば、送らず、受理しません（fail-closed）。",
      "problem": "黒塗りの処理を外部の基盤へ任せるとき、pin していない定義での処理、入力を上書きした結果や外への効果を持つ黒塗りを受け取ること、一部だけ塗れた結果（PARTIAL）を「全部塗れた」と扱うこと、そして黒塗りを開示の許可と取り違えることを防ぎます。",
      "use_when": [
        "黒塗りの処理を、pin した黒塗りの定義（redaction_manifests）に限って基盤へ運びたいとき。",
        "黒塗りの結果を入力と別の参照（output_ref）で受け取り、入力を上書きさせたくないとき。",
        "一部だけ塗れた結果（PARTIAL）を、全部塗れた結果と区別したいとき。PARTIAL では fully_redacted: false です。",
        "黒塗りの副作用を NONE か手元で取り消せるもの（LOCAL_REVERSIBLE）にとどめたいとき。DONE の result には redaction_state・fully_redacted・output_ref・disclosure_authorization が添えられます。"
      ],
      "do_not_use_when": [
        "何を黒塗りにするかを決めさせたいとき。その決定は核（redact-facts）の役目です。",
        "黒塗りが正しく塗れているか（見落とし）を確かめたいとき。この継手は receipt の状態と出力の参照だけを見ます。",
        "機微な情報を網羅的に検出する機能がほしいとき。直接の識別子の検出は、本人の参照に置かれたメールアドレスと電話番号の代表的な形だけを見る網で、すべての個人情報を見分けるものではありません。",
        "黒塗りを開示の許可として使いたいとき。disclosure_authorization は NOT_DERIVED か人・組織に固定され、継手は導きません。",
        "すぐに使える黒塗りの処理の基盤がほしいとき。実の基盤は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "H-05 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
      ],
      "what_it_checks": [
        "黒塗りの定義を検めます。pin していない定義（redaction manifest）の要求は送りません。",
        "入力を上書きした receipt（出力が入力と同じ参照）は受理しません。",
        "外への効果を持つ黒塗りの receipt は受理しません。黒塗りの副作用は NONE か LOCAL_REVERSIBLE で、FAILED は NONE です。",
        "開示の許可を導いたと名乗る receipt は受理しません。",
        "PARTIAL を「全部塗れた」と扱いません（fully_redacted: false）。",
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "塗る処理そのものはしません。黒塗りの処理は基盤の役目で、継手は要求を検めて運び、receipt を照合するだけです。実の基盤（黒塗りの処理）は含まず、port の実装と pin する一覧は接続するときに別の関門で決めます。",
        "何を黒塗りにするかは決めません。核（redact-facts）の役目です。",
        "黒塗りが正しく塗れているか（見落とし）は確かめません。receipt の状態と出力の参照だけを見ます。",
        "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
        "黒塗りは開示の許可ではありません。塗れたことも承認ではありません（approval: false）。",
        "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に RedactionPipeline の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の redaction_manifests。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-06",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "保持強制 — 保管ライフサイクル継手（Tsugite）",
      "summary": "保管ライフサイクル継手（Tsugite）は、保持（Retention）の要求（RetentionRequest。EVALUATE・RETAIN・EXPIRE）を、pin した保持方針と論理の target で検めてから保管の backend へ送り、receipt を契約どおりに照合する継手（つぎて）です。取り消せない削除（EXPIRE）は、設定 allow_expire（既定 false）で許し、リーガルホールドの無い対象にだけ送ります（fail-closed）。",
      "problem": "期限での削除・保全の要求を backend へ送るとき、許していない削除やリーガルホールド中の対象の削除を送ってしまうこと、期限切れの判定だけのはずが削除されたと扱うこと、backend の receipt を確かめないまま「消せた」と扱うことを防ぎます。",
      "use_when": [
        "保持方針に沿った保持の要求（RetentionRequest）を、検めてから保管の backend へ運びたいとき。",
        "削除（EXPIRE）の要求を、設定 allow_expire で許したときだけ送りたいとき。",
        "リーガルホールドの事実（核・人の決定）を受け取り、以後その対象の EXPIRE を送らないようにしたいとき。",
        "削除せずに、期限切れかどうかの判定（EVALUATE）だけを backend に求めたいとき。",
        "消せたこと（deleted: true）を、EXPIRE の receipt が EXPIRED と照合できたときだけ事実として扱いたいとき。"
      ],
      "do_not_use_when": [
        "期限が来たら自動で削除・保全する仕組みそのものがほしいとき。期限での削除・保全の実行は基盤の役目で、この継手は渡された保持の要求を運んで照合するだけです。",
        "保持期限やリーガルホールドをこの継手に決めさせたいとき。その決定は核と人の役目で、この継手は事実を受け取って守るだけです。",
        "期限で別の階層へ移したいとき。階層の移動は Archive の要求で、C-09 の面です。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定 allow_expire が true でないとき、EXPIRE を送りません。",
        "リーガルホールド中の対象には EXPIRE を送りません。",
        "EVALUATE の receipt が削除を伴っていれば受理しません。EVALUATE の EXPIRED は期限切れの判定だけで、削除ではありません。",
        "RETAIN に EXPIRED を返す receipt は、異常として受理しません。",
        "pin した一覧にない保持方針の要求は送りません。",
        "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
        "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
        "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。backend の障害は UNKNOWN とし、同じ request_id を二度送りません。"
      ],
      "what_it_does_not_do": [
        "期限での削除・保全を実行しません。実行は基盤の役目で、この継手は保持の要求を運んで照合するだけです。",
        "保持期限の決定・リーガルホールドの決定はしません。核（redact-facts・policy-facts）と人の役目で、この継手はその事実を受け取って守るだけです。",
        "消せたことは承認ではありません（approval: false）。",
        "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "H-06 専用の部品ではありません。C-05〜C-09 と F-04 と同じ 1 つの部品が担い、H-06 は C-08 の保持（RetentionRequest）の面です。試験と検査も共通です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に RetentionRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に decision・deleted・effect を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、保持方針・allow_expire の設定。副作用は EXPIRE が EXTERNAL_IRREVERSIBLE、ほかは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-07",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "リーガルホールド（運ぶだけ・判断も凍結もしない） — 監査・プライバシー継手（Tsugite）",
      "summary": "監査・プライバシー継手（Tsugite）は、リーガルホールド（LegalHold）の設定（PLACE）と解除（RELEASE）の要求を、人・組織から受け取った根拠の参照があるときだけ、pin した範囲で検めてリーガルホールドの仕組みの基盤へ運び、receipt を照合する継手（つぎて）です。運ぶだけで、削除の凍結そのものも、ホールドが必要かの判断もしません。外れれば送らず、受理しません（fail-closed）。",
      "problem": "リーガルホールドを外部の仕組みへ運ぶとき、人・組織の根拠が無いまま運んでしまうこと、人・組織以外や ERABAZU 自身を出どころとする根拠で運んでしまうこと、そして運べたことをリーガルホールドの権限と取り違えることを防ぎます。",
      "use_when": [
        "人・組織が出したリーガルホールドの根拠の参照を添えて、設定・解除の要求を基盤へ運びたいとき。根拠は recordAuthorityEvidence(evidence_ref, origin) で受け取ります（origin は HUMAN か ORGANIZATION だけ）。",
        "リーガルホールドの範囲を、pin した一覧（scope_refs）に限りたいとき。",
        "receipt の状態が op に答えている（PLACE→PLACED、RELEASE→RELEASED）か FAILED であることを照合してから、運べたとしたいとき。DONE の result には hold_state・legal_hold_authority が添えられます。",
        "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "リーガルホールドが必要かを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
        "根拠が本物の決定かを確かめたいとき。根拠の中身は検めず、人・組織から受け取った参照かどうかだけを見ます。",
        "運べたことをリーガルホールドの権限として使いたいとき。legal_hold_authority は NOT_DERIVED か人・組織に固定され、継手は導きません。",
        "削除の凍結を行う仕組みそのものがほしいとき。この継手は要求を運ぶだけで、実の基盤（リーガルホールドの仕組み）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
        "人・組織の根拠がまだ無いとき。根拠の無い要求は送りません（REFUSED）。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "H-07 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
      ],
      "what_it_checks": [
        "根拠の有無を検めます。人・組織の根拠が無い要求は送りません（REFUSED）。",
        "根拠の出どころを検めます。人・組織（HUMAN・ORGANIZATION）以外からの根拠、ERABAZU を出どころとする根拠では運びません。",
        "範囲を検めます。pin していない範囲の要求は送りません。",
        "リーガルホールドの権限を名乗る receipt は受理しません。",
        "receipt の状態を検めます。op に答えていない状態（PLACE→PLACED、RELEASE→RELEASED、FAILED のどれでもないもの）は受理しません。",
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "リーガルホールドが必要かを判断しません。法的な判断は人・組織と核の役目です。",
        "リーガルホールドの根拠を作りません（ERABAZU は根拠を作りません）。根拠の中身（本物の決定か）も検めず、人・組織から受け取った参照かどうかだけを見ます。",
        "削除の凍結（保全）を自分では行いません。要求を基盤へ運び、receipt を照合するだけです。実の基盤は含みません。",
        "保持・リーガルホールドの方針の事実を扱うのは核（policy-facts）、権限は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "運べたことはリーガルホールドの権限ではなく、承認でもありません（approval: false）。",
        "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で根拠を受け取った後、submit(request) に LegalHold の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の scope_refs。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-08",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "改ざん検知チェーン — 監査台帳継手（Tsugite）",
      "summary": "監査台帳継手（Tsugite）は、監査イベント封筒（AuditEventEnvelope）を前のハッシュ（previous_hash）でつなぐ改ざん検知（tamper-evident）の連鎖について、連鎖と digest を契約の式どおりに検めてから追記専用（append-only）の backend へ送り、返る受領（AuditAppendReceipt）の連鎖のハッシュを照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "ハッシュでつないだ監査の連鎖は、どこかで前のハッシュが書き換えられたり、digest の計算の仕方が食い違ったりすると、改ざんを見つける力を失います。この継手は、送る前の封筒と保管先の受領の両方を同じ契約の式で検め、式と合わない連鎖を送らず、受理しないことで、照合できない連鎖を信頼しないようにします。",
      "use_when": [
        "監査イベントを、前のハッシュでつないだ改ざん検知の連鎖として外部の追記専用 backend に残したいとき。",
        "連鎖のハッシュを、決まった式（RFC 8785 に制限した正準化と sha256）でそろえて計算・照合したいとき。",
        "保管先が返す受領の event_digest や連鎖のハッシュをそのまま信じず、送った封筒から計算し直して照合してから記録済みとしたいとき。",
        "連鎖の食い違いが見つかったら、人が確かめるまで追記を止めたいとき。",
        "封筒の検めと受領の照合だけを、backend を呼ばずに試したいとき。純粋な関数 checkEnvelope と checkReceipt が { ok, errors } を返します。"
      ],
      "do_not_use_when": [
        "透明性ログ（Merkle の包含証明）が要るとき。この継手が扱うのは前のハッシュでつなぐ連鎖で、包含証明は範囲外です。",
        "改ざんがあったかどうかの最終の判断を、この継手に任せたいとき。改変不能かの決定は核（provenance-facts・integrity-facts）の役目です。",
        "RFC 8785 に制限した正準化と sha256 ではない、別のハッシュの式を使いたいとき。式の違うハッシュは受理しません。",
        "監査イベントの本文そのものを連鎖に載せたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
        "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "前のハッシュ（previous_hash）を、台帳の先頭（head）と照らして検めます。previous_hash が書き換えられた封筒は、送らず、受理しません。",
        "連鎖のハッシュを契約の式で計算し直します。RFC 8785 に制限した正準化と sha256 で求め、正準化していない形で計算したハッシュは受理しません。",
        "連鎖の始まり（genesis）の規則を検めます。genesis の previous_hash_value は空文字として計算し、genesis の規則に反する封筒は送りません。",
        "digest を検めます。digest の対象（domain）を取り違えて計算したものは受理しません。",
        "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません（RECEIPT_REJECTED）。",
        "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
        "通番（sequence）の巻き戻し・飛びがあれば、送らず、受理しません。連鎖の順番が崩れた追記を通しません。"
      ],
      "what_it_does_not_do": [
        "改変不能かどうか、改ざんがあったかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
        "透明性ログ（Merkle の包含証明）は扱いません。",
        "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
        "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
        "追記の成功は承認ではありません（approval: false）。",
        "契約とハッシュの式を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "H-08 専用の部品ではありません。H-01（改変不能監査ログ）・C-04（追記専用台帳）・H-02（アクセスログ）と同じ 1 つの部品が担い、試験と検査も共通です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。検めだけなら checkEnvelope と checkReceipt が { ok, errors } を返します。利用側は追記専用 backend への port（append、任意で lookup）を用意します。副作用は EXTERNAL_IRREVERSIBLE（取り消せない書き込み）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-09",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "規制レポート出力（運ぶだけ・当局の受理ではない） — 監査・プライバシー継手（Tsugite）",
      "summary": "監査・プライバシー継手（Tsugite）は、規制報告（RegulatoryReport）の要求と報告の中身（bytes）を、pin した報告の形式・提出先と、digest・上限で検めてから規制当局の窓口の基盤へ運び、receipt を照合する継手（つぎて）です。運ぶだけで、報告の中身は利用側が渡し、届けたことは規制当局の受理ではありません。外れれば送らず、受理しません（fail-closed）。",
      "problem": "規制報告を外部の提出窓口へ運ぶとき、pin していない形式や提出先へ送ること、書き換わった中身や大きすぎる中身を送ること、許していない取り消せない提出をしてしまうこと、そして届けたことを規制当局の受理と取り違えることを防ぎます。",
      "use_when": [
        "規制報告の形式と提出先を、pin した一覧（report_profiles・report_destinations）に限りたいとき。",
        "報告の中身（bytes）の digest と上限（max_report_bytes）を確かめてから運びたいとき。max_report_bytes は報告を使うとき必須です。",
        "取り消せない提出を、設定 allow_irreversible: true のときだけに限りたいとき（既定は false）。",
        "生成（GENERATED）と届けた（DELIVERED）を副作用と照らして区別したいとき。GENERATED は NONE か LOCAL_REVERSIBLE、DELIVERED は EXTERNAL_* です。DONE の result には delivery_state・delivered・regulatory_acceptance が添えられます。"
      ],
      "do_not_use_when": [
        "規制当局が受理したかを知りたいとき。届けた報告は規制当局の受理ではなく、regulatory_acceptance は NOT_DERIVED か人・組織に固定され、継手は導きません。",
        "届いた先が pin した提出先かを receipt で確かめたいとき。規制報告の receipt には提出先の欄が無く、確かめられるのは送る前の pin だけです。",
        "報告の中身を作らせたいとき。中身は利用側が submit(request, bytes) の bytes として渡し、継手が確かめるのは digest と上限です。",
        "法的な判断をさせたいとき。この継手は法的な判断をせず、人・組織と核の役目です。",
        "すぐに使える提出の窓口がほしいとき。実の基盤（規制当局の窓口）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "H-09 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
      ],
      "what_it_checks": [
        "報告の形式と提出先を検めます。pin していない形式（report profile）・提出先（report destination）の要求は送りません。",
        "報告の中身を検めます。bytes の digest が合わないもの、上限を超えるものは送りません。",
        "取り消せない提出を検めます。allow_irreversible: true でなければ送りません。",
        "状態と副作用の対応を検めます。GENERATED なのに外への効果を名乗る receipt、DELIVERED なのに効果の無い receipt は受理しません。",
        "規制当局の受理を導いたと名乗る receipt は受理しません。",
        "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
        "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
      ],
      "what_it_does_not_do": [
        "規制当局の受理を導きません。届けたことは受理ではなく、承認でもありません（approval: false）。",
        "届いた先を receipt で確かめません。規制報告の receipt には提出先の欄が無く、送る前の pin だけで検めます。",
        "報告の中身について継手が確かめるのは digest と上限だけです。法的な判断はしません（人・組織と核の役目）。",
        "法的な権利・開示の許可は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
        "実の基盤（規制当局の窓口）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
        "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request, bytes) に RegulatoryReport の Request と中身の bytes を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg（pin する形式・提出先、max_report_bytes、allow_irreversible）。副作用は NONE〜EXTERNAL_*。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-10",
      "category": "H/監査・統制・プライバシー",
      "kind": "Primitive",
      "title": "系譜（lineage）の記録 — 系譜（lineage）の記録の部品（Primitive）",
      "summary": "系譜（lineage）の記録の部品（Primitive）は、集計に使った入力の集合（Ref の一覧）・集計器の参照と版・出力の bytes から系譜の記録（erabazu.lineage/1）を作り、あとで入力と出力から計算し直して照合する単独の部品です。入力の順は結果を変えず、重複は黙って除かずに断ります。照合の違いはエラーではなく、違った欄の一覧で返します。集計器は動かしません。",
      "problem": "数字の出どころを後で問われたときの裏付けになります。どの入力を使い、どの集計器の版で、どの出力ができたかを、入力の集合のハッシュ・集計器の版・出力のハッシュとして残します。重複した入力を黙って除かないので、どの入力を使ったかの数え違いを隠しません。",
      "use_when": [
        "集計の入力の集合・集計器の参照と版・出力の bytes・記録の時刻から、系譜の記録を作りたいとき（recordLineage）。",
        "入力の集合を、並べ順に左右されない digest にしたいとき。inputs を UTF-16 の符号単位の順に並べた配列の、RFC 8785 の正準 JSON の sha256 です。",
        "あとで入力と出力から計算し直し、記録と同じかを確かめたいとき（checkLineage。違いは matches: false と mismatched の一覧で、エラーではない）。",
        "どの欄が違ったか（input_set.count・input_set.digest・output.digest・output.size）を知りたいとき。",
        "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。recorded_at は呼ぶ側が渡します。"
      ],
      "do_not_use_when": [
        "集計器が正しいこと・出力が正しいことの証明が要るとき。この部品は集計器を動かさず、系譜は使ったものの記録です。",
        "本当にその版で動かしたか・その時刻かを確かめたいとき。tool の ref・version と recorded_at は渡された値をそのまま記録し、照合では形だけを検めて値は比べません。",
        "inputs の Ref が実在する入力を指すか、その中身を確かめたいとき。Ref の形だけを検め、中身が変わっても Ref が同じなら digest は同じです。",
        "記録そのものの改ざんを止めたいとき。それは署名（B-12）や封（H-11）の側の役目です。",
        "時差の付いた時刻（+09:00 など）やうるう秒で記録したいとき。recorded_at は UTC の Z だけで、ほかは INVALID_TIME で止めます。",
        "入力を 1,048,576 個より多く扱うとき。INVALID_INPUTS で止めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "inputs に同じ文字列が 2 回以上あるとき、黙って除かずに DUPLICATE_INPUT で止めます（文字列の完全一致で比べ、大文字と小文字は別の入力）。",
        "inputs が素の配列でない、穴・余分なプロパティ・getter の要素を持つ、Ref でない要素を持つ、1,048,576 個を超えるとき、INVALID_INPUTS で止めます。",
        "tool_ref が Ref でない、tool_version が 1〜200 文字でない・制御文字や対になっていないサロゲートを含むとき INVALID_TOOL、output_bytes が Uint8Array でないとき NOT_BYTES で止めます。",
        "recorded_at が RFC 3339 の UTC（Z）の形でない、または実在しない日時（2 月 30 日・24 時・60 秒など）のとき、INVALID_TIME で止めます。",
        "checkLineage の record の形が違うとき（欄の過不足・schema の違い・負の count・小文字 64 桁の hex でない digest など）、INVALID_RECORD と最初に見つけた欄の名前（field）で止めます。",
        "引数が素のオブジェクトでない・決まった欄のほかを持つとき INVALID_ARGUMENT、引数を読むこと自体が例外になるとき INSPECTION_FAILED で止めます。欄はそれぞれ一度だけ読みます。"
      ],
      "what_it_does_not_do": [
        "系譜は「何を使って何ができたか」の記録で、集計器が正しいこと・出力が正しいこと・記録が改ざんされていないことの証明ではありません。matches: true は、渡した入力と出力が記録と同じだったことだけを示します。",
        "記録を保管しません。状態・時計・外の接続を持ちません。",
        "inputs が 0 個でも記録を作ります（count 0、digest は \"[]\" の sha256）。0 個を断ることはしません。",
        "SHA-256 の計算そのものは Node.js の crypto に任せていて、ファズのオラクルも同じ crypto を使います。",
        "正準化は I-08 の部品のコードの写し（src/canonical.js）を使います。写しの中の分岐そのものは、この部品の試験では通りません（写しの試験は I-08 の側）。",
        "記録・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の来歴（provenance）の仕様の実装ではありません。"
      ],
      "integration": "src/lineage.js の recordLineage({ inputs, tool_ref, tool_version, output_bytes, recorded_at }) と checkLineage({ record, inputs, output_bytes }) を呼びます（投げません）。record は schema（erabazu.lineage/1）・input_set（count・digest）・tool（ref・version）・output（digest・size）・recorded_at です。返すのは record か matches・mismatched で、止めるときは { ok: false, errors: [{ code }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-11",
      "category": "H/監査・統制・プライバシー",
      "kind": "Component",
      "title": "封印（追記式の署名・日次の根） — 封印（追記式の署名・日次の根）の部品（Component）",
      "summary": "封印の部品（Component）は、Merkle 木（部品(Primitive) B-11）・手元の鍵での署名（部品(Primitive) B-12）・冪等書込の継手（C-05）・任意の固定の port（H-01 の形）を束ねる部品です。appendSeal は、呼ぶ側が渡した前の封印の記録の鎖を検め、足したファイルを含む新しい根に署名した記録を、次の seq の別の物として書きます。finalizeDay は区画ごとの鎖を検めて日の根を作り、seal/roots/<date>.json と署名した日の記録を書き、固定の port があれば固定します。どの結果も承認ではなく、時刻の権威でもありません。",
      "problem": "生データが後から書き換えられていないことの裏付けのための部品です。署名し直さない書き換えや信頼しない鍵の記録に続けて書くこと、証明や呼ぶ側の言う木の大きさを信じること、記録を上書きすること、固定できていないのに固定したと言うことを防ぎます。",
      "use_when": [
        "区画（folder_ref）に足したファイル（name と sha256、1〜10000 個）ごとに、前の記録の鎖に続く署名した封印の記録を <folder_ref>/seal/<seq>.json に 1 回だけ書きたいとき（appendSeal）。",
        "日ごとに、区画の根を folder_ref の順に束ねた日の根を作り、canonical({ seal_date, root }) の形のファイルと、署名した日の記録を書きたいとき（finalizeDay）。日の記録は時計を含まないので、同じ鎖からは同じ bytes になります。",
        "日の根を、注入した固定の port（H-01 の append と同じ形）で外へ固定し、その receipt を seal/roots/<date>.anchor.json に残したいとき。固定の port は根のファイルと日の記録が書けた後にだけ呼びます。",
        "署名した 1 件の記録に対して、ファイルの包含の証明を検めたいとき（verifyFile）。証明を鎖から作りたいとき（proveFile）。",
        "署名を、秘密鍵を閉じ込めた sign の port に任せ、この部品に秘密鍵を渡したくないとき。署名するのは keys の最初の鍵だけで、ほかの鍵は前の記録の検めにだけ使います。",
        "結果の分からなかった書込・固定を、同じ要求・同じ bytes・同じ envelope で送り直したいとき（resolvePending）。"
      ],
      "do_not_use_when": [
        "固定の port を付けずに、「この日時に存在した」ことの裏付けとして使いたいとき。固定の port が無ければ anchored: false で、記録は「これが署名した内容」だけです。sealed_at は注入した時計の値で、時刻の権威ではありません（信頼できる時刻は B-06）。",
        "署名を、ファイルの中身や取得の正しさ・業務の判断・承認として使いたいとき。署名は「keys の鍵の持ち主がこの bytes に署名した」ことだけです。",
        "鍵の置き場所・保管・入れ替え・失効まで任せたいとき。この部品の外で、どの鍵をいつまで信じるかは決めていません。",
        "verifyFile だけで鎖まで確かめたいとき。verifyFile は記録 1 件の署名と包含だけを検め、信頼する鍵で誤った tree_size に署名された記録は見分けられません。鎖の検めは appendSeal・finalizeDay・proveFile で行います。",
        "この部品に保管を読ませたいとき。この部品は保管を読みません。鎖は呼ぶ側が保管から読んで渡します。保管に無い版の記録を渡されると、それに続く記録を書き、以後その区画は SEAL_CHAIN_BROKEN で止まります（直すのは人）。",
        "固定の port 付きの finalizeDay のやり直しを、成功として受け取りたいとき。固定の記録は anchor_status を含むので中身が変わり、保管が断って WRITE_REFUSED で終わります。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・keys が 1〜16 個でない・key_id や指紋が重なる・port の関数が無いとき INVALID_OPTIONS、操作の入力の形が違うとき INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "渡した鎖が検めを通らないとき（正準化・欄・schema・folder_ref・seq・名前の重なり・tree_size・prev_entry_sha256・信頼する鍵の署名・前の根との一貫性・ファイルから計算し直した根）、SEAL_CHAIN_BROKEN で何も書きません。errors[0] に folder_ref・at・reason を添えます。",
        "tree_size と根は、署名した記録とファイルの数から取り直します。一貫性・包含の証明や呼ぶ側の言う大きさは使いません（RFC 9162 の証明は木の大きさを縛りません）。",
        "足すファイルの名前が重なる・鎖に既にあるとき DUPLICATE_NAME、finalizeDay で同じ folder_ref が 2 回あるとき DUPLICATE_FOLDER、区画の記録が 10000 件かファイルが 100000 個を超えるとき SEAL_LIMIT_EXCEEDED で止めます。",
        "sign の port が投げた・ok でないとき SIGN_FAILED、結果が Ed25519・最初の鍵の key_id と指紋でない・最初の鍵の公開鍵での往復の検めが通らないとき SIGNER_MISMATCH、B-11・B-12 の port の故障や形の違いのとき MERKLE_PORT_FAILED・VERIFY_PORT_FAILED で、書きません。",
        "書込の結果が分からないとき WRITE_UNKNOWN、送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。固定の port の結果が分からないとき ANCHOR_UNKNOWN、断ったとき ANCHOR_REFUSED、停止・receipt の照合の失敗・形の違いのとき ANCHOR_FAILED で、anchored: true にしません。保留の間の appendSeal・finalizeDay は SEAL_PENDING です。",
        "verifyFile で、記録が正準化できない・形が違う・信頼する鍵の署名でないとき ENTRY_UNVERIFIED、証明の tree_size が記録と違う・leaf_index が大きすぎるとき PROOF_NOT_FOR_ENTRY、proveFile で名前が鎖に無いとき NAME_NOT_SEALED です。",
        "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
      ],
      "what_it_does_not_do": [
        "封印の部品は継手ではありません。自分の Request/Receipt は無く、書込は束ねた C-05 の継手が自分の契約で、固定は注入した port がその契約で起こします（台帳への追記・Git の push は取り消せない効果です）。この部品自身は外へ触れません。",
        "束ねた部品の意味（根が正しい ≠ 中身が正しい・署名 ≠ 承認・固定した ≠ 権威ある時刻で刻んだ）は、それぞれの部品の側に残ります。",
        "どの結果も、承認でも権限でも時刻の権威でもありません（approval: false・authority: NOT_DERIVED・timestamp_authority: NOT_DERIVED、derives_authority: false）。",
        "外への固定の強さは、注入した固定の port とその先の強さまでです。この部品は port が固定したと言ったことと receipt を記録するだけで、固定先を自分では確かめません。",
        "上書き・削除の要求は送りません。秘密鍵を見ません（sign の port の中だけ）。",
        "単体試験とファズは代役です（ファズの署名の代役は Ed25519 ではなく、速さのための SHA-512 の代役）。本物の B-11・B-12・C-05・H-01 との結線は、別の結線の検めで確かめました。Git の固定は、成功する経路だけを結線で確かめ、断り・衝突・non-fast-forward の経路は試していません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の透明性ログや署名の製品の雛形ではありません。"
      ],
      "integration": "src/seal.js の createSeal(options) で作り、{ ok, errors, instance } を返します。options は target・request_prefix・keys・ports・任意の timestamp です。操作は appendSeal・finalizeDay・verifyFile・proveFile・resolvePending・status で、結果に approval: false が付きます。port は merkle（B-11 の 5 つの関数）・sign（秘密鍵を閉じ込めた closure）・verify と publicKeyFingerprint（B-12）・write.submit（C-05 の継手のクライアント）・任意の anchor.append です。資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "H-12",
      "category": "H/監査・統制・プライバシー",
      "kind": "Tsugite",
      "title": "封印の根の Git への固定 — 封印の根の Git への固定の継手（Tsugite）",
      "summary": "封印の根の Git への固定の継手（Tsugite）は、日次の封印の根のファイル（seal/roots/YYYY-MM-DD.json）を、Git の決まった枝へ前の版（parent_commit）の子として commit し、fast-forward で push する要求（SealAnchorRequest）を検めてから port へ運び、返った receipt（SealAnchorReceipt）を照合する継手（つぎて）です。追記だけで、外れたら送らず、受理しません（fail-closed）。",
      "problem": "「いつの時点の根か」を外から照らせるようにします。そのとき、根を上書きしてしまうこと、同じ日付に別の根を固定してしまうこと、push の結果が見えないまま二重に commit してしまうこと、commit の日時を時刻の権威と取り違えることを防ぎます。",
      "use_when": [
        "封印の部品が書いた日次の根のファイルを、Git のリポジトリへ追記だけで固定したいとき。",
        "送る前に、要求の root_digest からファイルの中身 canonical({seal_date, root}) を作り直し、その sha256 が file_digest と同じことを確かめたいとき。",
        "1 つの日付に 1 つの根を守りたいとき。食い違い（CONFLICT）の日付は、人の判断まで送りません。",
        "push の結果が見えないとき、相手の側（branch の先頭と path のファイル）を読んで解いてから送り直したいとき（port.inspect と SealInspectRequest / SealInspectReceipt）。"
      ],
      "do_not_use_when": [
        "根そのもの（Merkle の計算・署名）を作りたい・確かめたいとき。それは封印の部品の役目で、この継手は根の digest からファイルの digest を作り直して照らすだけです。",
        "信頼できる時刻で刻みたいとき。commit の日時もホストの push の時刻も観測です。信頼できる時刻は B-06（RFC 3161）の継手で別に刻みます。",
        "履歴の書き換え（上書き・force-push・amend・削除）をしたいとき。要求にも設定にもその欄はありません。",
        "特定の Git ホスティング（GitHub など）の API や git コマンドの使い方の雛形がほしいとき。この継手はその雛形ではありません。",
        "すぐに使える固定先がほしいとき。実の Git・remote への commit・push は 0 回で、port の実装・資格情報・ブランチ保護の設定は、接続するときに別の関門で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定と要求を検めます。repository・branch・committer は設定の一覧（repositories・branches・committer_refs）の中だけです。branch_ref は git check-ref-format に合うものだけで、暦に無い seal_date は送りません。空の一覧は何も許しません。",
        "ファイルの digest を照らします。作り直した sha256 と file_digest が違う要求は送りません。committed_file_digest が違う receipt、ANCHORED の parent_commit が依頼と違う receipt、changed_paths がちょうど [path] でない receipt は受理しません。",
        "追記だけを守ります。history_rewrite が NOT_PERFORMED でない receipt は受理しません。設定に知らないキー（force_push など）があれば、何も送りません。",
        "日付の状態を照合します。知っている日付に別の根は送りません。知っている日付への 2 度目の ANCHORED、知っている根と違う ALREADY_ANCHORED は受理せず、停止します（HALTED）。",
        "commit の桁を照合します。commit_id は依頼の parent_commit と同じ桁（40 桁か 64 桁、リポジトリの形式）のときだけ受け取ります。",
        "効果を増やす receipt、FAILED のほかで効果の不明な receipt、状態の表の外の receipt、別の要求の receipt は受理せず、停止します。ANCHORED の効果は EXTERNAL_IRREVERSIBLE、ほかは NONE（FAILED は NONE か UNKNOWN）です。",
        "結果の見えない push（port の障害、FAILED・UNKNOWN の receipt）は UNKNOWN（未解決）とし、成功にも失敗の確定にもしません。相手の側を読めないときは送り直さず、解けるまでほかの要求も送りません。",
        "push の資格情報は参照（committer_ref）だけです。committer_refs に封印の署名の鍵の参照（seal_signing_key_refs）が入っている設定は、誤りとして拒否します。"
      ],
      "what_it_does_not_do": [
        "commit の中身が本当にその親とその変更か、push が fast-forward だったか、読んだ答えが本当の Git の状態かは確かめません（port の申告。この継手は git のオブジェクトを読みません）。",
        "日付の状態はこの継手の中だけで、再起動で消えます。この継手が知らない日付の上書きは見分けられません。上書き・force-push の禁止は、repository のブランチ保護で別に守ります。",
        "commit の日時・host_time の正しさも順序も確かめません。どちらも時刻の権威ではありません（commit_date_authority・timestamp_authority は NOT_DERIVED）。",
        "根そのものの正しさは確かめません。CONFLICT の解決は人の判断です。",
        "固定できたことは承認ではなく、権威ある時刻で刻んだことでも、根が正しいことでもありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/seal-anchor-git/v1（SealAnchorRequest / SealAnchorReceipt、読む口 SealInspectRequest / SealInspectReceipt）でつなぎます。createSealAnchorClient({ cfg, port, clock }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Git の port（submit、任意の lookup・inspect）と cfg の一覧。副作用: ANCHORED だけ EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-01",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "入力スキーマ検証 — 入力スキーマ検証の部品（Primitive）",
      "summary": "入力スキーマ検証の部品（Primitive）は、JSON の値を、JSON Schema 2020-12 の全体ではなく決まった一部（profile json-schema-subset/v1）で検める単独の部品です。使えるキーワードは決まった 28 個だけで、ほかのキーワード（format を含む）はスキーマごと断ります（黙って読み飛ばしません）。結果は { ok, errors } で、止めるときは最初の 1 件のコードと位置（JSON Pointer）を返します（fail-closed）。",
      "problem": "入力の形の妥当性を検めるときに、知らないキーワード・外の $ref・壊れた値を黙って読み飛ばして「通った」ことにしてしまうことを防ぎます。使えるキーワードを固定し、一部の外のものはスキーマごと断ります。",
      "use_when": [
        "JSON の値の形（type・required・properties・additionalProperties・長さ・範囲・pattern・enum・const など）を、決まった一部のキーワードで検めたいとき（validate）。",
        "スキーマそのものが一部の中に収まっているかを、インスタンスなしで先に確かめたいとき（checkSchema）。",
        "止めた理由を、コード・instance_path・schema_path（JSON Pointer）の最初の 1 件として、決まった順で受け取りたいとき。",
        "getter を呼ばず、スキーマとインスタンスを 1 回だけ読んだ写しで検めたいとき（途中で中身が変わらない）。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。validate と checkSchema は例外を投げません。"
      ],
      "do_not_use_when": [
        "JSON Schema 2020-12 の全体が要るとき。使えるのは決まった 28 のキーワードだけで、format は検めずに断ります。",
        "いまの実物の契約のスキーマ（00a の 17 本）をそのまま検めたいとき。17 本はどれも一部の外（format・runtime_validator_contract・uniqueItems・hash_computation・外の $ref）を使うので、スキーマごと断られます。",
        "外の文書・$id による基底の URI・$anchor・$dynamicRef を使う $ref が要るとき。$ref は \"#\" と \"#/$defs/<名前>\" だけです。",
        "信頼できないスキーマの pattern をそのまま使うとき。正規表現の爆発（ReDoS）は止めず、評価の回数の上限は正規表現の中の時間を数えません。",
        "文字列・配列の長さや const・enum の比較の大きさに上限が要るとき。上限は持たず、I-06 の部品か使う側で決めます。",
        "2^53 以上の整数を integer として扱いたいときや、見た目が同じ文字列を同じとして扱いたいとき。integer は安全な整数だけで、Unicode の正規化はしません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED。RegExp の u フラグの Unicode の版はエンジンに依ります）。"
      ],
      "what_it_checks": [
        "一部の外のキーワード（format を含む）が、どこにあっても（使われない $defs の中でも）あるとき、根でない $schema・$id・$defs があるとき、スキーマごと UNSUPPORTED_KEYWORD で止めます。",
        "$schema が 2020-12 でないとき UNSUPPORTED_DIALECT、$ref の形が違うとき（外の文書・アンカー・% ・~ の書き方）UNSUPPORTED_REF、名前が $defs に無いとき UNRESOLVED_REF で止めます。",
        "インスタンスを進めずに回る $ref があるとき、たどり着くかどうかに関わらず REF_CYCLE で止めます。",
        "キーワードの値の形が違うとき（空の enum・重なる required や type・負の数・配列の形の items・if の無い then）INVALID_SCHEMA、pattern が作れないとき INVALID_PATTERN で止めます。",
        "スキーマが JSON の値でないとき（getter を含む）SCHEMA_NOT_JSON、インスタンスが JSON の値でないとき（undefined・関数・NaN・bigint・Date・穴のある配列・symbol のキー・getter など）INSTANCE_NOT_JSON で止めます。",
        "入れ子が 256 段を超えるとき SCHEMA_TOO_DEEP・INSTANCE_TOO_DEEP、評価の深さが 512 に届くとき EVALUATION_TOO_DEEP、評価の回数が 1,000,000 を超えるとき EVALUATION_LIMIT で止めます。not・anyOf・oneOf・if の中で届いても「通らない」に化けません。",
        "インスタンスがスキーマに合わないとき、TYPE_MISMATCH・REQUIRED_MISSING・ADDITIONAL_PROPERTY・PATTERN_MISMATCH・ONE_OF_MANY_MATCHED など、キーワードごとの決まったコードで止めます。2^53 以上の整数は integer に当てはまりません。",
        "値を調べること自体が例外になるとき（Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。Proxy が答えを変えても、1 回だけ読んだ写しを検めるので途中で結果が変わりません。"
      ],
      "what_it_does_not_do": [
        "JSON Schema の全体ではなく一部だけを扱います（決まった 28 のキーワード）。format は検めずに断り、外の文書・$anchor・$dynamicRef の $ref も断ります。",
        "実物の契約のスキーマ（00a の 17 本）は、どれも一部の外として断られます。一部は黙って広げておらず、一部を広げるか、契約の側を一部に合わせるかは別の決定です。",
        "公式の JSON-Schema-Test-Suite にはかけていません。既知の答えは、この部品の試験に書いたものです。",
        "pattern の正規表現の爆発（ReDoS）は止めません。信頼できないスキーマの pattern は使う側で制限します。",
        "スキーマに合うことは形の検めで、値の中身が正しいことでも、受け入れてよいことでも、許可でもありません（意味の判断は Kernel、承認は人。result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の JSON Schema のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/schema-validate.js の validate(schema, instance) を呼びます（投げません）。checkSchema(schema) はスキーマだけを検め（投げません）、assertValid は合わなければ SchemaValidateError を投げます。受け取るのは、決まったキーワードだけのスキーマと、JSON の値のインスタンスです。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code, instance_path, schema_path }] }（最初の 1 件。code は error_taxonomy）。外の $ref は読みに行きません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-02",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "出力エンコード — 出力エンコードの部品（Primitive）",
      "summary": "出力エンコードの部品（Primitive）は、文字列を、決めた 1 つの出力先（context）の中で文字として読まれる形にする単独の部品です。context は html_text・html_attribute_quoted・url_component・json_in_html_script の 4 つだけです。決めていない context・文字列でない値・単独のサロゲート・長すぎる入力は、決まったコードで止めます（fail-closed）。HTML の無害化（sanitize）ではありません。",
      "problem": "文字列を HTML・URL・<script> の中に書き出すときに、context を決めずに推測で符号化してしまうことや、壊れた文字列（単独のサロゲート）を黙って置き換えてしまうことを防ぎます。context は名前で明示させ、既定の context はありません。",
      "use_when": [
        "文字列を HTML の要素の中身（テキスト）に入れたいとき（html_text。& < > \" ' を実体参照にします）。",
        "文字列を二重引用符で囲んだ HTML の属性の値に入れたいとき（html_attribute_quoted。html_text の 5 つに加えて ` も符号化します）。",
        "文字列を URL の 1 つの部分（パスの 1 区切り・クエリの名前や値）に入れたいとき（url_component。RFC 3986 の unreserved のほかを %XX にします）。",
        "文字列を HTML の <script> 要素の中に、JSON の文字列リテラルとして置きたいとき（json_in_html_script。< > & U+2028 U+2029 を \\u の形にし、</script> や <!-- で要素が閉じられません）。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。encode は例外を投げず、逆向きに戻すと元の text になります。"
      ],
      "do_not_use_when": [
        "タグや属性を残したい HTML の入力を無害化したいとき。この部品は HTML の無害化（sanitize）ではなく、すべてを文字として書きます。",
        "引用符で囲まない属性・イベント属性（onclick など）・style 属性・URL を取る属性（href・src の javascript: など）の値の意味を防ぎたいとき。html_attribute_quoted はそれを防ぎません。",
        "URL 全体の検め（スキームの許可・ホストの検め）が要るとき。url_component は URL の 1 つの部分のためです。",
        "HTML の属性の中・JavaScript の文字列やテンプレートリテラルの中・style などの生テキストの要素に書くとき。json_in_html_script は <script> 要素の中のためです。",
        "CSS・JavaScript の識別子・XML・SQL・シェル・ヘッダーなどの context に書くとき。これらは扱いません。",
        "1,048,576 UTF-16 符号単位より長い文字列を扱うとき。INPUT_TOO_LARGE で止めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "context が決めた 4 つのどれでもないとき（大文字や空白の違い・Object.prototype の名前・文字列でない値を含む）、UNKNOWN_CONTEXT で止めます。",
        "text がプリミティブの文字列でないとき（数・null・undefined・String オブジェクト・配列・Symbol・bigint・Proxy）、NOT_A_STRING で止めます。",
        "text が 1,048,576 UTF-16 符号単位より長いとき、INPUT_TOO_LARGE で止めます。",
        "text に単独のサロゲート（対になっていない U+D800〜U+DFFF、逆順の対・末尾の単独を含む）があるとき、どの context でも INVALID_UNICODE で止めます。U+FFFD に置き換えません。",
        "止める順は context → 文字列か → 長さ → 単独のサロゲートで、最初に当たった 1 つだけを返します。"
      ],
      "what_it_does_not_do": [
        "「injection 防止」を名前に持つ項目ですが、この部品は決めた 1 つの context の中で文字を文字として書く符号化だけで、HTML の無害化（許すタグを残す処理）は含みません。",
        "どの値をどの context に入れるか（テンプレートの組み立て）は決めません。使う側、または別の部品に残ります。",
        "ある context のための符号化は、別の context での安全ではありません。",
        "ブラウザの実際の読み方（parse）は実機で確かめていません。HTML・URL・JSON の仕様の規則から決め、Node.js v24.21.0 の組み込みの関数と独立オラクルで確かめました。",
        "Unicode の正規化はしません。見た目が同じでも、符号単位が違えば別の出力です。",
        "符号化できたことは、その文字列を別の context に入れてよいことでも、HTML として無害にしたことでも、内容を受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のテンプレートエンジンやエスケープのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/encode.js の encode(text, context) を呼びます（投げません）。受け取るのは、プリミティブの文字列 text（1,048,576 UTF-16 符号単位まで）と、html_text・html_attribute_quoted・url_component・json_in_html_script のどれかの context です。返すのは { ok: true, errors: [], text }（符号化した文字列）か { ok: false, errors: [{ code }] }（code は error_taxonomy。text は無い）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-03",
      "category": "I/検証・無害化・安全",
      "kind": "Tsugite",
      "title": "マルウェアスキャン — マルウェア検査 receipt 継手（Tsugite）",
      "summary": "マルウェア検査 receipt 継手（Tsugite）は、アップロードされた添付などを pin した scanner へ提出し、返ってきた検査の受領（ScanReceipt）を、対象の digest・pin した engine と版・検査の時間枠・語彙・契約の const で検める継手（つぎて）です。検査そのものは外部の engine が行い、この継手は scan engine を含みません。どの結果でも検疫は保ち、安全とは言いません（NOT_DETECTED ≠ SAFE。fail-closed）。",
      "problem": "アップロードの検疫で、偽った・古い・すり替えた検査結果を通してしまうこと、そして「検出なし」を「安全」と取り違えて検疫を外してしまうことを防ぎます。検出が無くても安全は導かず（safety: NOT_DERIVED_FROM_SCAN）、検疫の解除は Policy・人の決定に残します。",
      "use_when": [
        "アップロードされた添付などを、外部の scan engine に検査させ、その結果を検めてから扱いたいとき。",
        "検査の受領を、対象の digest・pin した scanner と engine と版・提出からの時間枠で照合し、偽った・古い・すり替えた結果を通したくないとき。",
        "検査の結果にかかわらず検疫を保ち、解除は Policy・人の決定に任せたいとき。",
        "判定の無い・不明な検査結果を、clean 扱いにしたくないとき。"
      ],
      "do_not_use_when": [
        "マルウェアを検査する engine そのものがほしいとき。この継手は scan engine を含みません。検査は外部の engine が行い、どの engine につなぐかはまだ決まっていません。",
        "「検出なし」を「安全」として使いたいとき。NOT_DETECTED でも安全とは言わず（safety: NOT_DERIVED_FROM_SCAN）、検疫を保ちます。",
        "検疫の解除をこの継手にさせたいとき。解除は Policy・人の決定で、この継手は扱いません。",
        "モデレーション（I-04）が要るとき。別の部品です。",
        "検査の結果を、承認や信頼・認可の根拠として扱いたいとき。どの結果も承認ではなく、trust・authorization は導きません（NOT_DERIVED）。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "提出を検めます。許可していない content-type、大きさの上限を超えるもの、中身の改ざん（digest と合わない bytes）は提出しません（REFUSED）。",
        "scanner と engine を検めます。pin していない scanner は受け付けず、pin した一覧に無い engine や版の受領は受理しません。pin していない engine の受領が来れば、以後は停止します（HALTED）。",
        "受領の対象を照合します。request_digest を偽った受領、対象の digest をすり替えた受領は受理せず、以後は停止します（HALTED）。",
        "時間枠を検めます。提出より前の受領と、提出から max_scan_seconds を過ぎた受領は、古い受領として受理しません。",
        "語彙と証拠を検めます。知らない判定、証拠の無い DETECTED、safe・trusted・authorized を主張する受領は受理しません。",
        "INCONCLUSIVE・UNKNOWN の判定は SCAN_INCONCLUSIVE とし、clean 扱いにしません。",
        "scanner の障害は UNKNOWN とします。同じ提出は二重に出さず、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "マルウェアを検査しません。scan engine を含まず、検査そのものは外部の engine が行います。実 scan engine は未定で、port の実装と pin する対象（engine・版・受領の形式）は、接続するときに別の関門で決めます。",
        "検出を受けた後の案件の整理はしません。核（case-support）の役目です。検疫の解除は Policy・人の決定です。",
        "安全を導きません。どの結果にも safety: NOT_DERIVED_FROM_SCAN、quarantine: KEEP_UNTIL_POLICY_DECISION、approval: false が付きます。",
        "検疫を解除しません。解除は Policy・人の決定です。",
        "モデレーション（I-04）は扱いません。別の部品です。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "ウイルス対策の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createScanClient({ cfg, port }) の scan(ScanSubmitRequest, bytes) は、DETECTED_QUARANTINE_KEPT・NOT_DETECTED_QUARANTINE_KEPT・SCAN_INCONCLUSIVE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 外部の scan engine への port（submit と、任意で lookup）。副作用は外部への提出（EXTERNAL_REVERSIBLE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-04",
      "category": "I/検証・無害化・安全",
      "kind": "Tsugite",
      "title": "コンテンツ・モデレーション — コンテンツ・モデレーションの継手（Tsugite）",
      "summary": "コンテンツ・モデレーションの継手（Tsugite）は、1 つの中身（投稿・画像など）について、外のモデレーションの事業者・AI に観察を頼む要求（ModerationRequest）を検めてから運び、返った receipt（ModerationReceipt）を契約どおりに照合する継手（つぎて）です。返すラベルは観察だけで、消す・止める・公開する・人の判断・承認は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "外の事業者へ中身を渡すことは取り消せない開示（EXTERNAL_IRREVERSIBLE）です。確かめていないバイト列や、開示の根拠が覆わない事業者・中身の種類へ渡してしまうこと、そして返ったラベルを削除・公開の拒否・人の判断と取り違えることを防ぎます。",
      "use_when": [
        "投稿・画像などの中身を、外のモデレーションの事業者・AI に見てもらい、その観察（ラベル）を判定の素材として受け取りたいとき。",
        "事業者・方針の版・モデルの版を設定で固定し、固定の外の版で観察させたくないとき。版がずれた receipt も受け取りません。",
        "開示の根拠（disclosure_basis_ref）が、その事業者と content_type を覆うときだけ中身を外へ渡したいとき。",
        "送る中身のバイト列が、要求に載せた content_digest と同じものだと確かめてから渡したいとき。"
      ],
      "do_not_use_when": [
        "観察の結果から、削除・公開停止などの処置を自動で行いたいとき。この継手の結果は事実の観測で、処置ではありません（enforcement_action は NOT_DERIVED）。",
        "手元でモデルを動かして観察したいとき。外の事業者・AI に頼む継手で、手元の観察は別の Primitive の範囲です。",
        "中身を外へ渡してはいけない場面。送れば取り消せない開示（EXTERNAL_IRREVERSIBLE）になり、結果の分からない要求（UNKNOWN）でも開示はすでに起きたかもしれません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える事業者への接続がほしいとき。実の事業者・AI には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "設定の固定を検めます。設定が無い・一覧が空・表が欠ける、または事業者・方針・方針の版・モデル・モデルの版が固定の外、ほかの事業者のモデルなら、送りません。",
        "開示の根拠を検めます。設定に無い根拠、その事業者または content_type を覆わない根拠、Ref の形でない根拠では、送りません。",
        "中身のバイト列を検めます。バイト列が無い、sha256 が content_digest と違う、確かめた後に変えられた場合は送りません。要求に中身そのものを載せた要求も拒否します。",
        "参照と要求の形を検めます。空・ドライブの絶対パス・..・先頭の /・制御文字・U+2028/2029 を含む参照、Ref でない request_id、media type でない content_type、追加の欄は送りません。設定が形の悪い参照を固定していても通しません。",
        "receipt を照合します。request_digest の偽り、別の要求・別の事業者の receipt、content_digest の写しの違い、受け取った中身の digest が違うのに CONTENT_MISMATCH でない receipt、方針・モデルの版のずれは受理しません。",
        "ラベルの形を検めます。語彙の外の verdict_state・level、ラベルの無い OBSERVED、OBSERVED でないのにラベルがある receipt、category の重なり、0〜10000 の整数でない score_bp は受理しません。",
        "処置・人の判断・公開の判断を導いた receipt（enforcement_action・human_judgement・publication_decision が NOT_DERIVED でないもの）、状態の許さない効果や UNKNOWN の効果を持つ receipt は受理せず、停止します（HALTED）。",
        "結果の分からない要求は同じ要求の再送（lookup）で解き、解けるまでほかの要求は送りません。同じ request_id の別の要求、未解決の要求のすり替え、送る間に変えられた要求は送りません。"
      ],
      "what_it_does_not_do": [
        "モデレーションの処置をしません。この継手は観察の依頼を運ぶ継手で、削除・公開停止・違反の判断の機能は含みません。結果は事実の観測で、どの結果にも enforcement_action・human_judgement・publication_decision の NOT_DERIVED と approval: false が付きます。",
        "ラベルの正しさ、方針への当てはまり、score_bp の意味は確かめません。観察の中身は事業者の答えです。違反と判断するのは、別の人の判断と核の役目です。",
        "開示の根拠（同意・承認の記録）を作らず、それが有効か・期限内か・撤回されていないかも確かめません。設定に載っていることだけを見ます。根拠は部品の外の人・組織の役目です（APPROVAL_OUTSIDE_PART）。",
        "事業者が実際に受け取ったバイト列は、事業者が received_content_digest を返したときだけ確かめられます。返さなければ、確かめたバイト列を port に渡したところまでです。",
        "時刻（observed_timestamp）は形だけを検め、receipt が古い・使い回しかは見ません。照合の記録と未解決の要求はこの継手の中だけにあり、再起動で消えます。",
        "事業者の認証に使う資格情報は持ちません。資格情報は port 側が持ちます（HELD_BY_PORT）。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のモデレーション API の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/content-moderation/v1 でつなぎます。createModerationClient({ cfg, port }) の submit(request, bytes) に ModerationRequest と中身のバイト列を渡すと、DONE（result に verdict_state・labels ほか）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。cfg には固定の一覧 4 つ（allowed_providers ほか）が要ります。利用側が用意するもの: 事業者への port（submit、任意で lookup）と資格情報。副作用: EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-05",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "ファイル型検証 — ファイル型検証の部品（Primitive）",
      "summary": "ファイル型検証の部品（Primitive）は、bytes の先頭の magic bytes を版つきの表（pdf・png・jpeg・gif・zip・gzip・webp の 7 つ）と照らして型と MIME を判り、申告の MIME・許す MIME の一覧と突き合わせる単独の部品です。受け取るのは bytes だけです。判らない型・申告との違い・許していない型は、決まったコードで止めます（fail-closed）。",
      "problem": "偽装した拡張子や申告の Content-Type を信じて、実体の違うファイルを受け取ってしまうことを防ぎます。bytes から判った型と申告を突き合わせ、許す一覧を必須にして、表に無い MIME を一覧に書いた設定の誤りも止めます。",
      "use_when": [
        "受け取ったファイルの bytes から、先頭の magic bytes で型と MIME を判りたいとき（detect）。",
        "申告の MIME（declared_mime）が bytes の実体と完全に一致し、許す MIME の一覧（allowed_mimes）にあるときだけ受け取りたいとき（check）。",
        "大きなファイルでも一定の手間で検めたいとき。読むのは先頭の 12 bytes までです。",
        "例外を投げない呼び方が要るとき。check は { ok, errors, detected } を返し、例外を投げず、options の getter を呼びません。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。"
      ],
      "do_not_use_when": [
        "中身の安全（マルウェア・悪意のある PDF の JavaScript・zip bomb など）を検めたいとき。それは I-03 の範囲です。",
        "ファイルの残りが正しい形か（壊れていないか・開けるか）を確かめたいとき。判るのは先頭の magic bytes だけです。",
        "docx・xlsx など zip を土台にした形式の中の形式を判りたいとき。application/zip とだけ判り、中の形式の MIME を申告すると TYPE_MISMATCH になります。",
        "表の 7 つのほかの型（text・json・svg・mp4・webm・tiff・bmp・heic・7z・rar など）を扱いたいとき。UNKNOWN_TYPE で止めます。",
        "申告の MIME の大文字・引数（; charset= など）・別名（image/jpg など）の読み替えを任せたいとき。読み替えはしないので、呼ぶ側が外して小文字にしてから渡します。",
        "bytes の大きさの上限が要るとき。上限は持たず、I-06 の部品か使う側で決めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "bytes（Uint8Array）でないとき（文字列・数の配列・ArrayBuffer・ほかの型付き配列）、check は NOT_BYTES で止めます（detect は FileTypeError を投げます）。",
        "表のどの型の magic bytes とも合わないとき（途中で切れた頭・1 byte 違い・GIF88a・小文字の %pdf-・zip の空の書庫や分割の印・gzip の別の方式・RIFF の WAVE・位置のずれ）、UNKNOWN_TYPE で止めます。",
        "判った MIME が declared_mime と完全には一致しないとき（別名・引数付き・大文字・zip に Office の MIME を申告）、TYPE_MISMATCH で止めます。",
        "判った MIME が allowed_mimes に無いとき、NOT_ALLOWED で止めます。",
        "allowed_mimes が無い・空・穴がある・配列に似たオブジェクト・表に無い MIME を含むとき、INVALID_OPTIONS で止めます。",
        "declared_mime が文字列でないとき、opts に知らないキー（symbol を含む）・getter があるとき、Proxy の罠・クラスのインスタンスのとき、INVALID_OPTIONS で止め、例外を投げません。",
        "止めるときのエラーはいつも 1 つで、検める順番は bytes・options・型が判るか・申告・許す一覧です。"
      ],
      "what_it_does_not_do": [
        "型が判ることは、中身の安全の検めではありません。マルウェアの検査（I-03）・大きさの上限（I-06）・中の構造の検め（画像の復号など）は別の部品で、組み合わせるのは使う側です。",
        "magic bytes が合っても、ファイルの残りが正しい形である証明ではありません。PDF の xref・PNG の chunk・zip の中央ディレクトリなどは読みません。",
        "継手の中にある magic bytes の写し（content.js 2 つ）を、まだこの部品に置き換えていません。pdf・png・jpeg の検めは作業場で 1 回突き合わせて違いは無かったものの、写しは text/plain・application/json も扱い Buffer だけを受け取る点が違い、置き換えは別の作業です。",
        "zip を土台にした形式の中の形式、空の zip・分割の zip は判りません。",
        "型が判ったことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のファイル型判定ライブラリ（file-type・libmagic など）の写しや使い方の雛形ではありません。"
      ],
      "integration": "src/filetype.js の check(bytes, opts) を呼びます（投げません）。受け取るのは bytes（Uint8Array。Buffer を含む）と、allowed_mimes（必須）と declared_mime（任意）だけの素のオブジェクト opts です。返すのは { ok: true, errors: [], detected: { type, mime } } か { ok: false, errors: [{ code }], detected }（code は error_taxonomy）。detect(bytes) は { type, mime } か null を返します（bytes でなければ投げます）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-06",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "サイズ・数量制限 — サイズ・数量制限の部品（Primitive）",
      "summary": "サイズ・数量制限の部品（Primitive）は、値の大きさを 6 つの決まった単位（bytes・utf8_bytes・utf16_units・code_points・items・keys）のどれかで数え、上限・下限と比べる単独の部品です。単位ごとに受け取る値の型が決まっていて、合わなければ止めます。上限は必須で、超えれば TOO_LARGE、足りなければ TOO_SMALL で止めます（fail-closed）。単位は呼ぶ側が決めて渡します。",
      "problem": "過大な入力を断る上限の検めで、数えられない値を 0 と数えてしまうこと、文字列を bytes として・配列に似たオブジェクトを配列として数えてしまうこと、単独のサロゲートを含む文字列を UTF-8 の長さで黙って数えてしまうこと、上限の無い・空の範囲を受け取ってしまうことを防ぎます。",
      "use_when": [
        "本文・名前・配列・オブジェクトの大きさに、使う側が決めた単位と上限を当てたいとき（例: 本文は utf8_bytes で 1 MiB、名前は code_points で 64）。",
        "同じ文字列でも単位によって数が違うことを、単位を明示して扱いたいとき（絵文字 1 つは utf8_bytes で 4、utf16_units で 2、code_points で 1）。",
        "上限・下限の境目を決めて検めたいとき。上限は既定で含み（max_inclusive）、下限は既定 0 で常に含みます。",
        "I-08 の正準化・B-07 のハッシュ・I-05 のファイル型の部品に渡す前に、大きさの上限を当てたいとき。それらの部品は大きさの上限を持ちません。",
        "例外を投げない呼び方が要るとき。check は例外を投げず、値と options を書き換えず、getter を呼びません。",
        "時計・乱数・環境の locale に左右されず、状態を持たない処理として、手元で呼びたいとき。"
      ],
      "do_not_use_when": [
        "見た目の 1 文字（書記素。👨‍👩‍👧 や 🇯🇵）の数で制限したいとき。書記素は数えません。",
        "見た目が同じ文字列を同じ長さとして扱いたいとき。Unicode の正規化はしないため、é と e + 結合文字は数が違います。",
        "入れ子の中（配列の中の配列、オブジェクトの中のキー）まで含めた大きさで制限したいとき。items・keys は一段目だけを数えます。",
        "値をメモリーに読み込む前に、大きすぎる入力を止めたいとき。この部品は、すでにメモリーにある値を数えます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "大きさが上限を超えるとき（max_inclusive が false なら上限と等しいときも）TOO_LARGE、下限より小さいとき TOO_SMALL で止め、数えた大きさ（size）を返します。",
        "値の型が単位に合わないとき（bytes に文字列・ArrayBuffer・ほかの型付き配列、文字列の単位に bytes・String オブジェクト、items に配列に似たオブジェクト・Set、keys に配列・Map・Date・クラスのインスタンスなど）、VALUE_TYPE_MISMATCH で止めます。",
        "utf8_bytes で、文字列に単独のサロゲート（対になっていない U+D800〜U+DFFF）があるとき、INVALID_UNICODE で止めます。",
        "unit が 6 つの単位のどれでもないとき（大文字・別名を含む）、UNKNOWN_UNIT で止めます。",
        "max が無い、max・min が 0 以上の安全な整数でない、min > max、または受け取れる大きさが 1 つも無い範囲のとき、INVALID_LIMITS で止めます。",
        "opts が素のオブジェクトでない、知らないキー・getter がある、max_inclusive が真偽値でないとき INVALID_OPTIONS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。"
      ],
      "what_it_does_not_do": [
        "名前の「過大入力の遮断」のうち、大きすぎる値の読み込みそのものは止められません。値を渡す前にメモリーに載せる手間は、この部品の外の話です。",
        "どの単位で何を上限にするかは決めません。使う側（各継手・契約・Kernel の方針）が決めます。",
        "書記素の数・Unicode の正規化・入れ子の中の大きさは扱いません。",
        "継手の中の大きさの上限の検めと、工房の台帳の I-06 の記録を、この部品に置き換えてはいません。置き換えは別の作業です。utf8_bytes は、単独のサロゲートのある文字列では Buffer.byteLength と違い、止めます。",
        "Proxy の罠は単体試験の数件だけで確かめました（ファズには入れていません）。",
        "範囲の中に収まったことは、値の中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の検証ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/size.js の check(value, opts) を呼びます（投げません）。opts は { unit, max, min?, max_inclusive? } だけの素のオブジェクトで、unit は 6 つの単位のどれか、max は必須の 0 以上の安全な整数です。返すのは { ok: true, errors: [], size } か { ok: false, errors: [{ code }], size }（code は error_taxonomy、size は TOO_LARGE・TOO_SMALL のときだけ数、ほかは null）。measure(value, unit) は 0 以上の整数を返し、受け取れない値で SizeError を投げます。副作用（NONE）・状態・外の接続・資格情報・port は無く、依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-07",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "重複排除 — 重複排除の部品（Primitive）",
      "summary": "重複排除の部品（Primitive）は、渡された一覧（{ key } の配列）と、渡された索引（known_keys）の中で、同じ key の重複を見分ける単独の部品です。はじめて出た番号を kept に、2 回目からと索引にあるものを duplicates（{ index, of }）に分けます。key は UTF-16 の符号単位のまま比べます。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
      "problem": "reconcile と対になる重複の見分けで、形の違う entry を黙って読み飛ばすこと、__proto__・constructor などの名前の key を取り違えること、件数や key の長さに上限の無い入力を受け取ることを防ぎます。同じ入力からは、いつでも同じ答えになります。",
      "use_when": [
        "1 回の呼び出しで渡す一覧の中で、同じ key の重複を見分けたいとき。どの番号も kept と duplicates のどちらか一方にだけ、1 回入ります。",
        "前に見た key を known_keys として渡し、それとの重複も of: \"known\" として見分けたいとき。",
        "I-08 の正準化や B-07 のハッシュで作った key を、符号単位の完全一致で比べたいとき。どの値から key を作るかは使う側が決めます。",
        "受け取れない入力を、黙って読み飛ばさずに、コードと位置（JSON Pointer）で止めたいとき。",
        "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。"
      ],
      "do_not_use_when": [
        "保存済みの索引や、ほかの呼び出しとの重複を見たいとき。比べるのは引数の中だけで、持続する索引・大域の索引は読まず書きません（保管の継手の側）。",
        "見た目が同じ key（é と e + 結合文字、A と a、前後の空白の違い）を同じものとして扱いたいとき。正規化・大文字小文字の同一視・空白の除去はしません。同じにしたいなら、使う側が決まった規則で key を作ってから渡します。",
        "100,000 件を超える一覧を 1 回で扱いたいとき。使う側で分けます。分けた呼び出しの間の重複は、前の key を known_keys に渡さない限り見分けません。",
        "entry の中身（key のほかのプロパティ）で重複を判断したいとき。重複の判断は key だけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "entries が素の配列でないとき（配列の派生クラスを含む）INVALID_ENTRIES、100,000 件を超えるとき TOO_MANY_ENTRIES で止めます。",
        "要素が素のオブジェクトでない（穴・getter・クラスのインスタンス・null prototype を含む）、自分のデータのプロパティ key が無い（getter・継いだもの）とき、MALFORMED_ENTRY で止めます。",
        "key が文字列でない・空のとき INVALID_KEY、4096 の UTF-16 の符号単位を超えるとき KEY_TOO_LONG で止めます。",
        "known_keys が素の配列でないとき INVALID_KNOWN_KEYS、100,000 件を超えるとき TOO_MANY_KNOWN_KEYS、同じ key が 2 回あるとき DUPLICATE_KNOWN_KEY（2 回目の位置）で止めます。",
        "options が素のオブジェクトでない、known_keys のほかの名前がある、known_keys が getter のとき INVALID_OPTIONS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
        "止めたときは、最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、kept・duplicates は返しません。"
      ],
      "what_it_does_not_do": [
        "名前の「重複排除」のうち、保存されたデータから重複を消すことはしません。引数の一覧の番号を kept と duplicates に分けるだけです。保存済みの索引を引く・書くのは保管の継手の側です。",
        "名前の「同一物の検出」のうち、中身が同じかどうかは見ません。key の符号単位の完全一致だけで、正規化・大文字小文字の同一視もしません。",
        "渡されていない索引（ほかの呼び出し・保存済みのもの）との重複は分かりません。",
        "上限ちょうどの 100,000 件を受け取ることは、単体試験だけで確かめました（ファズは上限を超えるものだけ）。",
        "重複でないことは、中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の重複排除のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/dedupe.js の dedupe(entries, options) を呼びます（投げません）。受け取るのは、{ key } の素のオブジェクトの素の配列（100,000 件まで）と、省略か { known_keys } だけの options です。返すのは { ok: true, errors: [], kept, duplicates }（kept は番号の小さい順、duplicates は { index, of } で of は番号か \"known\"）か { ok: false, errors: [{ code, path }] }（code は error_taxonomy、path は JSON Pointer）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するもの（port）はありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-08",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "正準化 — 正準化の部品（Primitive）",
      "summary": "正準化の部品（Primitive）は、JSON の値を RFC8785-restricted の規則で正準の文字列（UTF-8 bytes）にする単独の部品です。キーを UTF-16 の符号単位の順に並べ、空白を入れず、数は安全な整数だけを受け取ります。受け取れない値は、決まったコードと位置で止めます（fail-closed）。",
      "problem": "署名・突合の前処理で、キーの順・空白・数の書き方のゆれや、受け取れない値を黙って読み飛ばすことによって、digest の元になる bytes が変わってしまうことを防ぎます。同じ値からは、いつでも同じ文字列ができます。",
      "use_when": [
        "署名・突合の前処理として、JSON の値から digest の元になる正準の bytes を作りたいとき（canonicalBytes）。",
        "受け取れない値（小数・undefined・関数・素でないオブジェクト・穴のある配列など）を、黙って読み飛ばさずに、コードと位置（JSON Pointer）で止めたいとき。",
        "例外を投げない呼び方が要るとき。tryCanonicalize は { ok, errors, text } を返し、例外を投げません。",
        "時計・乱数・環境の locale に左右されない、決定的な出力が要るとき。",
        "外の境界・状態を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
      ],
      "do_not_use_when": [
        "小数・指数の数を含む値を正準化したいとき。数は安全な整数（-(2^53-1)〜2^53-1）だけで、RFC 8785 の全体ではありません。",
        "見た目が同じ文字列を同じものとして扱いたいとき。Unicode の正規化はしないため、é と e + 結合文字は別の文字列です。",
        "カタログの一言「表現ゆれの統一（RFC8785 等）」のような広い統一を求めるとき。この部品がするのは、RFC 8785（JCS）のうち数を安全な整数に限った profile の範囲です。",
        "1000 段を超える入れ子の値を扱うとき。TOO_DEEP で止めます。",
        "値の大きさの上限が要るとき。文字列・配列・キーの長さの上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
        "Node.js v24.21.0 のほかの版・エンジンで使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "小数・安全な範囲の外の整数・NaN・Infinity のとき、NOT_A_SAFE_INTEGER で止めます。",
        "undefined（値そのもの・配列の要素・オブジェクトのメンバー）のとき UNDEFINED_VALUE、関数・symbol・bigint のとき UNSUPPORTED_TYPE で止めます。",
        "配列の派生クラス、穴のある配列、要素のほかのプロパティを持つ配列のとき、NOT_A_PLAIN_ARRAY・SPARSE_ARRAY・EXTRA_ARRAY_PROPERTY で止めます。",
        "素でないオブジェクト（Date・Map・Buffer・null prototype・クラスのインスタンスなど）、symbol のキー、列挙できないプロパティを持つオブジェクトのとき、NOT_A_PLAIN_OBJECT・SYMBOL_KEY・NON_ENUMERABLE_PROPERTY で止めます。",
        "getter・setter のプロパティがあるとき、呼ばずに ACCESSOR_PROPERTY で止めます。値を調べる間に中身が変わりません。",
        "入れ子が 1000 段を超えるとき、スタックを溢れさせずに TOO_DEEP で止めます。",
        "値を調べること自体が例外になるとき（Proxy の罠など）、tryCanonicalize は INSPECTION_FAILED を返し、例外を投げません。",
        "止めたときは、コード（code）と止めた位置（path。JSON Pointer（RFC 6901）で、根は空文字）を返します。"
      ],
      "what_it_does_not_do": [
        "RFC 8785 の全体（小数・指数の数の書き方）は扱いません。数は安全な整数だけです。",
        "Unicode の正規化はしません。見た目が同じでも、符号単位が違えば別の文字列です。",
        "どのフィールドを digest に入れるか・どの値を除くかは決めません。使う側（各継手・契約）に残ります。",
        "継手の中にある正準化の写し（20 個）を、まだこの部品に置き換えていません。写しが正しく扱う値では byte 単位で同じ出力（または同じく止める）ですが、写しが壊れた JSON や欠けた出力を作る値（穴のある配列・symbol のキーなど）は、この部品は止めます。写しの側の扱いはそのままで、置き換えは互換表を確かめた後の別の作業です。",
        "正準の文字列ができたことは、値の中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "Node.js v24.21.0 のほかのエンジンでの、JSON.stringify の文字列の書き方は確かめていません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の JSON ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/canonical.js の tryCanonicalize(value) を呼びます（投げません）。canonicalize・canonicalBytes は正準の文字列・UTF-8 の Buffer を返し、受け取れない値で CanonicalizationError を投げます。受け取るのは JavaScript の値 1 つ（null・真偽値・文字列・安全な整数・密な配列・素のオブジェクトの入れ子）。返すのは { ok: true, errors: [], text } か { ok: false, errors: [{ code, path }] }（code は error_taxonomy、path は JSON Pointer）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-09",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "可読性の検め — 可読性の検め（gzip・JSON・TSV・PDF／画像の浅い検め）の部品（Primitive）",
      "summary": "可読性の検めの部品（Primitive）は、取得した bytes が開けるか（gzip の解凍、UTF-8、JSON の読み、TSV の列の数、PDF・PNG・JPEG の印）を検め、事実として返す単独の部品です。読めないことはエラーではなく、readable: false と reason の事実で返します。開けないものを止める判断（門番）はしません。",
      "problem": "読めないファイルは、開くまで気づきません。この部品は、壊れた gzip・不正な UTF-8・壊れた JSON でも例外を投げずに事実として返し、解凍した後の大きさを 1 GiB で止めて、gzip の爆弾で際限なくメモリーを使わないようにします。format を中身から推し量らないので、宣言と違う中身を別の型として読み替えません。",
      "use_when": [
        "取得した bytes が、宣言された型として開けるかを事実として残したいとき（probeReadability）。",
        "gzip で圧縮された bytes を、解凍してから検めたいとき（compression: \"gzip\"。解凍できなければ GUNZIP_FAILED）。",
        "JSON の最上位の型（top_type）や、TSV の行の数と列の数（rows・columns）を事実として知りたいとき。",
        "PDF・PNG・JPEG に印があるか（PNG は IHDR の width・height・bit_depth・color_type も）を浅く確かめたいとき。",
        "読めないことをエラーにせず、なぜ読めなかったか（reason と、TSV_RAGGED のときの行番号など）を受け取りたいとき。",
        "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。ファイルは開かず、bytes は呼ぶ側が渡します。"
      ],
      "do_not_use_when": [
        "PDF・画像が描けること、ページ・画像のデータが壊れていないこと、暗号化されていないことを確かめたいとき。readable: true は印があることだけです（markers_only: true）。",
        "UTF-8 のほかの文字コード（Shift_JIS など）を読みたいとき。charset が utf-8 でなければ CHARSET_NOT_SUPPORTED で、変換も推測もしません。charset を宣言しない Shift_JIS は NOT_UTF8 になります。",
        "中身の先頭の bytes から型を当ててほしいとき。format は宣言された media type の type/subtype だけで決めます。",
        "TSV の引用符（\"…\" の中の \\t や改行）を扱いたいとき。\\t は常に区切りとして数えます。",
        "PDF の前に bytes がある、%%EOF の後に 1024 bytes を超える bytes が続く、JPEG の EOI の後に bytes が続くファイルを、開けるものとして扱いたいとき。印が無いという事実になります。",
        "256 MiB を超える入力や、解凍して 1 GiB を超える入力を検めたいとき。INPUT_TOO_LARGE（limit 付き）で止めます。これは読めないという事実ではなく、検めなかったという意味です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "入力がオブジェクトでない、bytes が Uint8Array でない（文字列・数の配列・ArrayBuffer・ほかの型付き配列）、プロパティを読むこと自体が例外になるとき、NOT_BYTES で止めます。",
        "declared_media_type が契約 raw-fetch/v1 の形の文字列でない・255 文字を超えるとき INVALID_MEDIA_TYPE、compression が \"gzip\" でも \"none\" でもないとき（省略を含む）INVALID_COMPRESSION で止めます。",
        "宣言の引数が RFC 9110 の parameter として読めない・charset が 2 つ以上あるとき MEDIA_TYPE_PARAMETERS_UNREADABLE、json・tsv・text で charset が utf-8 でないとき CHARSET_NOT_SUPPORTED を、bytes を読む前に返します。",
        "UTF-8 は fatal に復号し、過長・サロゲート・U+10FFFF 超・途中で切れた並びは NOT_UTF8 です。先頭の BOM は外さず、BOM 付きの JSON は JSON_PARSE_FAILED です。",
        "空でない行の列の数がそろわない TSV は TSV_RAGGED で、最初にそろわなかった行の番号と列の数を facts に返します。",
        "PDF は先頭の %PDF- と最後の 1024 bytes の中の %%EOF、PNG は 8 bytes の署名・長さ 13 の IHDR・その CRC-32、JPEG は先頭の FFD8（SOI）と末尾の FFD9（EOI）を検めます。無ければ PDF_MARKERS_MISSING・PNG_SIGNATURE_OR_IHDR_BAD・JPEG_MARKERS_MISSING です。",
        "入力が 256 MiB を超える、解凍した後が 1 GiB を超える、JSON の本文が 256 MiB を超えるときは、検めずに INPUT_TOO_LARGE と limit（input・gunzip_output・json_text）を返します。",
        "宣言された media type が対応の表に無いときは FORMAT_UNKNOWN です。bytes は渡された view の範囲だけを読みます。"
      ],
      "what_it_does_not_do": [
        "readable は開けるかどうかの事実だけで、中身が正しいこと・完全なこと・安全なこと・受け入れてよいことを意味しません。pdf・png・jpeg では印があることだけを意味します。",
        "開けないものを止める判断（門番）はしません。止めるかは使う側が決めます。",
        "ファイルを開く・取得する・保存することはしません。",
        "gzip の解凍は zlib に任せ、ファズのオラクルも zlib を使います。zlib そのものの正しさは、この部品の試験の範囲の外です。",
        "メモリーが足りない環境での振る舞いは確かめていません。JSON.parse が文法以外の理由で失敗した場合も JSON_PARSE_FAILED になります。",
        "可読性の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のファイル形式のライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/decode-probe.js の probeReadability({ bytes, declared_media_type, compression }) を呼びます（投げません）。bytes は Uint8Array（256 MiB まで）、declared_media_type は解凍した後の中身の型の宣言（契約 raw-fetch/v1 の形、255 文字まで）、compression は \"gzip\" か \"none\"。返すのは { ok: true, errors: [], readable, format, facts, reason } か { ok: false, errors: [{ code }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みの zlib だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-10",
      "category": "I/検証・無害化・安全",
      "kind": "Component",
      "title": "取得データの検査（版つき） — 取得データの検査（3 段・4 状態・版つき）の部品（Component）",
      "summary": "取得データの検査の部品（Component）は、取得の直後に raw を 3 段（完全性・可読性・実用性）で検め、4 つの状態（done・need_input・failed・need_consent）のどれかを、検査器の版つきの別の記録に残す部品です。記録の検め（部品(Primitive) N-09）・検めた読出し（C-01 の継手）・可読性の検め（部品(Primitive) I-09）・source ごとの抽出器・冪等書込（C-05 の継手）を束ねます。raw へは読むだけで、消さない・止めません。done は 3 項目が取り出せたことで、正しいことではありません。",
      "problem": "取り込みの成否は、後から見返せません。この部品は、検査の結果を検査器の版つきの別の記録に残します。あわせて、検査が raw を止めてしまうこと、port の障害なのに嘘の done を書くこと、認定の一覧に無い継手の取得を読むこと、取り出した値（PII を含みうる）を結果に移すことを防ぎます。",
      "use_when": [
        "取得の直後に、fetch.json の記録（fetch_record）とフォルダー（folder_ref = raw/<source>/<YYYYMMDDTHHMMSSZ>）を渡し、検査の結果を probe/<source>/<folder_stamp>/<probe_version>.json に 1 回だけ書きたいとき（probeFetch）。",
        "取得の失敗・記録が無い・N-09 が記録を受け取らないときは failed、tsugite_id が認定の一覧（certified_tsugite）に完全一致しないときは need_consent として、raw を読まずに記録したいとき。",
        "完全性を期待する sha256 と大きさで、可読性を I-09 で、実用性を source ごとの抽出器で 3 項目（取引年月日 occurred_on・金額 amount・相手先 counterparty）が取り出せたかで検め、足りない段を need_input と理由で知りたいとき。",
        "検査器を新しい版にして過去の取得に当て直し、古い記録を残したまま新しい記録を作りたいとき。結果のキーは probe_version を含みます。",
        "need_input の山から、次に作る継手と検査器を見つけたいとき（抽出器が無い source は need_input / NO_EXTRACTOR）。",
        "結果の分からなかった書込を、同じ要求で送り直したいとき（resolvePending）。"
      ],
      "do_not_use_when": [
        "done を、取り出した値が正しいこと・形が正しいこと（日付・金額の書き方）として使いたいとき。done は 3 項目が取り出せたことだけです。",
        "検査を門番にして、合わない raw を止めたり消したりしたいとき。この部品は raw を書かず・消さず・止めません。結果は別の記録です。",
        "ファイルが描けること・壊れていないことまで確かめたいとき。可読性は I-09 の浅い検め（pdf・png・jpeg は印があるか）で、I-09 の上限を超えたファイルは検めていません（READABILITY_NOT_CHECKED）。",
        "抽出器が正しく読んでいるか、認定の一覧が正しいかまで任せたいとき。どちらもこの部品の外で、この部品は tsugite_id の文字列の完全一致だけを見ます。",
        "application/gzip の中身の型を確かに当てたいとき。中身の型は名前の拡張子の表から引くので、表に無い・名前が嘘のときは違う型で検め、need_input になることがあります。",
        "信頼できない抽出器を隔離して動かしたいとき。抽出器は同じプロセスで動き、時間とメモリに上限はありません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違うとき INVALID_OPTIONS、probeFetch の入力の形が違うとき（folder_ref の形・fetch_failed が真偽値でない など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "N-09 の port が投げた・答えが食い違うとき（この記録についての答えでない、schema・区画・files の形が違う）CHECK_PORT_FAILED、記録の source と folder_stamp が folder_ref と違うとき FOLDER_MISMATCH で、結果を書きません。",
        "完全性の段で、保管物が無いとき INTEGRITY_MISSING、GET_REJECTED か自分で計算し直した sha256 が違うとき INTEGRITY_DIGEST_MISMATCH、大きさが byte_size と違うとき INTEGRITY_SIZE_MISMATCH として、全ファイルを並べて need_input にします。",
        "完全性の段の読出しの結果が分からないとき READ_UNKNOWN、断り・停止・形の違い・bytes でない値のとき READ_FAILED、I-09 の port が投げた・答えの形が違うとき READABILITY_PORT_FAILED で止め、結果を書きません（errors[0].name にそのファイル）。",
        "可読性の段で、読めないファイルがあれば need_input / UNREADABLE、検めていないものがあれば need_input / READABILITY_NOT_CHECKED です。実用性の段で、抽出器が投げた・配列でない答えなら EXTRACTOR_FAILED、記録が 0 件なら NO_RECORDS、3 項目の欠けがあれば MISSING_FACTS（件数と欠けた項目の名前）です。",
        "同じ版の結果のキーに、このインスタンスが前に別の中身を送っているとき PROBE_RESULT_EXISTS で送りません。書込の結果が分からないとき PROBE_WRITE_UNKNOWN（保留の間は PROBE_PENDING）、送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。",
        "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED で何も読まず書かず、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
      ],
      "what_it_does_not_do": [
        "取得データの検査の部品は継手ではありません。自分の Request/Receipt は無く、結果の書込という外への効果は、束ねた C-05 の継手が自分の契約で起こします（raw の読出しは NONE）。この部品自身は外へ触れません。",
        "束ねた部品の意味（記録を受け取れた ≠ 取得の正しさ・読めた ≠ 中身の正しさ・開けた ≠ 壊れていない・書けた ≠ 受け入れ）は、それぞれの部品の側に残ります。抽出器そのものと取り出した値の正しさは、この部品に入りません。",
        "結果は承認ではなく、正しいことも示しません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "取り出した値は結果の記録に書きません。書くのは件数と欠けた項目の名前とファイル名・番号だけです。",
        "raw のキーがその区画の保管物を指すかは backend の設定の役目です。ほかのプロセスが書いた同じ版の結果は、この部品ではなく継手と backend が断ります（どちらでも上書きはしません）。",
        "本物の N-09・I-09・C-01・C-05 には、この部品の試験では接続していません。単体試験とファズは代役です。状態（送った結果のキーの控え・保留）はプロセスの中だけで、再起動で消えます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のデータ品質ツールの雛形ではありません。"
      ],
      "integration": "src/probe.js の createProbe(options) で作り、{ ok, errors, instance } を返します。options は probe_version・certified_tsugite・result_target・request_prefix・ports・任意の timestamp です。probeFetch は state・reason・result・result_key などを返し、結果に approval: false が付きます。port は checkFetchRecord（N-09）・probeReadability（I-09）・区画ごとの read.<class>.get（C-01 の継手のクライアント）・write.submit（C-05 の継手のクライアント）・任意の extractors です。資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "I-11",
      "category": "I/検証・無害化・安全",
      "kind": "Primitive",
      "title": "選んだ入力の組み立てと UTF-8 の容量 — 選んだ入力の組み立てと UTF-8 の容量の部品（Primitive）",
      "summary": "選んだ入力の組み立てと UTF-8 の容量の部品（Primitive）は、明示して選んだ項目だけを、選んだ順に決まった区切りでつなぎ（prefix + 部分.join(separator) + suffix）、UTF-8 の bytes を数えて、使う側が渡した上限と比べる単独の部品です。選ぶときは版を名指しし、範囲は単位（UTF16 か CODE_POINT）を名指しします。超えたら OVER_LIMIT と超えた量を返し、切りません。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
      "problem": "送った文が何から組み立てられ、上限に入ったかは、後から見えません。この部品は、選んでいない項目を足すこと、古い版の選択を使うこと、上限を超えた文を黙って切る・縮める・分けることを防ぎ、選んだもの・除いたもの・版・digest の一覧を返します。",
      "use_when": [
        "明示して選んだ項目（id と revision を名指し）だけを、選んだ順に separator でつなぎ、前後に prefix・suffix を付けた request を組み立てたいとき（assemble）。選んでいない項目は入りません。",
        "組み立てた request の UTF-8 の bytes を、使う側が渡す上限 limit_bytes（1〜1,073,741,824）と比べたいとき。上限ちょうどは WITHIN_LIMIT です。上限の既定値は持たず、使う側が渡します。",
        "項目の一部だけを、単位を名指しした範囲 { unit, start, end } で選びたいとき。unit は UTF16 か CODE_POINT で、同じ数でも別の文字を指しうるので、単位を推し量りません。",
        "何を組み立てたかを後から照らしたいとき。request_digest（request の UTF-8 の sha256）と、版・中身・範囲・区切りのどれかが変われば変わる selection_digest、選んだ順の included と、選ばなかった excluded を返します。",
        "役ごとの投影（P-12）の後に、送る文字列を固定したいとき。値 1 つの大きさを数える I-06 とは違い、選んだものだけを組み立てて版と digest を固定します。",
        "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。同じ入力には同じ答えを返します。"
      ],
      "do_not_use_when": [
        "上限を超えた文を切ったり縮めたり分けたりして収めたいとき。OVER_LIMIT のとき request_text は null で、どの部分も切らず除きません。選び直しは使う側です。",
        "どれを選ぶか（選ぶ権限・同意・目的）を部品に確かめさせたいとき。選ばれたものを組み立てるだけで、選ぶ権限・同意・目的が正しいかは見ません。",
        "JSON などの外の包みやフレームを含めた大きさを数えたいとき。数えるのは prefix + 部分.join(separator) + suffix だけで、外の包みは使う側が別に検めます。",
        "組み立てた文を送ったり保存したりしたいとき。この部品は送信・保存をしません。外の包みの組み立てと送信は使う側に残ります。",
        "items・selection が 10,000 件を超える、text・区切りが 4,194,304 の UTF-16 の符号単位を超える入力を扱いたいとき。上限を超えると決まったコードで止めます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が素のオブジェクトでない、知らない名前・getter がある、limit_bytes か separator が無いとき INVALID_OPTIONS、limit_bytes が 1〜1,073,741,824 の整数でないとき INVALID_LIMIT、separator・prefix・suffix が文字列でない（箱の文字列を含む）か長すぎるとき INVALID_FRAMING で止めます。",
        "items が素の配列でないとき INVALID_ITEMS、10,000 件を超えるとき TOO_MANY_ITEMS、要素の形が違うとき MALFORMED_ITEM、id・revision・text の形が違うとき INVALID_ID・INVALID_REVISION・INVALID_TEXT、text が長すぎるとき TEXT_TOO_LONG、同じ id が 2 回あるとき DUPLICATE_ITEM で止めます。",
        "selection の形が違うとき INVALID_SELECTION・TOO_MANY_SELECTED・MALFORMED_SELECTION、選んだ id が items に無いとき UNKNOWN_ITEM、選んだ版が items の版と違うとき REVISION_MISMATCH（古い選択を使いません）、同じ id を 2 回選んだとき DUPLICATE_SELECTION で止めます。",
        "range が { unit, start, end } でない、unit が UTF16・CODE_POINT でない（小文字を含む）、0 ≤ start < end の安全な整数でないとき INVALID_RANGE、end が text の長さを超えるとき RANGE_OUT_OF_BOUNDS、UTF16 の範囲の端がサロゲートの対の間にあるとき RANGE_SPLITS_SURROGATE で止めます。",
        "選んだ部分か separator・prefix・suffix に単独のサロゲートがあるとき（UTF-8 にできない）INVALID_UNICODE、組み立てた request が空か空白（改行・全角の空白を含む）だけのとき BLANK_REQUEST、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
        "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、組み立てた結果は返しません。",
        "結果を、別の書き方の独立オラクル（bytes は TextEncoder、符号位置は Array.from、単独のサロゲートは isWellFormed、項目は Map を使わず前から探す）と突き合わせるファズ（1,000,000 回）で、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "WITHIN_LIMIT は「渡された上限に入る」だけで、送ってよい・同意があるという意味ではありません。外の包み（JSON・フレーム）の大きさは数えていません。",
        "中身の digest（content_digest）は渡された text の digest で、元の資料と同じかは見ません。",
        "上限ちょうどの 10,000 件・4,194,304 の符号単位を受け取ることは、単体試験だけで確かめました（ファズは小さな入力だけ）。",
        "外の境界・状態・時計・乱数を持たず、送信・保存をしません。副作用は NONE で、資格情報も使いません。",
        "組み立てと数えの結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のプロンプト組み立てのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/selected-input.js の assemble(items, selection, options) を呼びます（投げません）。items は { id, revision, text }、selection は { id, revision, range? } の配列、options は { limit_bytes, separator, prefix?, suffix? } です。返すのは { ok: true, errors: [], status, request_text, request_digest, selection_digest, included, excluded など } か { ok: false, errors: [{ code, path }] } です。副作用・状態・外の接続・資格情報・依存パッケージ・port は無しです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-01",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "PDF 生成 — 文書・メディア変換継手（Tsugite）",
      "summary": "PDF 生成の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へPDF 生成の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。PDF を作るのは backend で、この継手は PDF を作りません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "帳票・証憑を PDF で出すために外の変換 backend を使うとき、偽装した入力や pin していない出力 profile で送ってしまうこと、そして戻った PDF が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。出力の bytes の digest を自分で計算して receipt と突き合わせます。",
      "use_when": [
        "帳票・証憑を PDF で出す処理を外の変換 backend に任せ、その手前と後ろの検めがほしいとき。",
        "PDF の出力 profile と params を pin して、決めた形でしか作らせたくないとき。",
        "backend が返した PDF の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
        "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "PDF を作る仕組みそのものがほしいとき。この継手は PDF を作らず、実 backend も含みません。",
        "できた PDF の見た目が元と同じか（忠実さ）や、PDF として壊れていないかを確かめたいとき。どちらも確かめません。",
        "できた PDF を保管したいとき。保存は object store 継手（C-01）の役目です。",
        "電子契約が要るとき。電子契約（J-06）は作っておらず、この部品は J-06 を担いません。電子契約の操作を求める要求は送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "PDF の生成そのものはしません。PDF を作るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "変換エンジンの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-02",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "OCR・テキスト抽出 — 文書・メディア変換継手（Tsugite）",
      "summary": "OCR・テキスト抽出の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へOCRの要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。文字を読み取るのは backend で、この継手は OCR をしません。読めた文字は検証済みの事実とも信頼とも扱わず、一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "画像や紙から文字を取り出して検索・照合の前処理に使うとき、偽装した入力を送ってしまうこと、抽出の結果（extracted_ref）が無いのに成功と扱ってしまうこと、読めた文字を確かめた事実のように扱ってしまうことを防ぎます。",
      "use_when": [
        "画像や PDF の文字の読み取りを外の OCR サービスに任せ、その手前と後ろで検めたいとき。",
        "OCR_EXTRACT の成功には extracted_ref が必ず付いていてほしいとき。付いていない成功は受理しません。",
        "読めた文字を検索・照合の前処理の材料として受け取り、事実として扱うかの判断は核（evidence 系）に任せたいとき。",
        "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "読み取った文字が正しいことを確かめたいとき。OCR で読めた内容の正しさは確かめず、検証済みの事実とも信頼とも扱いません。",
        "OCR エンジンそのものがほしいとき。この継手は OCR をせず、実 backend も含みません。どの OCR サービスにつなぐかは未定です。",
        "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "OCR そのものはしません。文字を読み取るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "OCR で読めた内容の正しさは確かめません（契約の const のとおり、主張しません）。読めた文字を事実として扱うかは evidence 系の核の判断です。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "変換エンジンの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED（OCR では extracted_ref 付き）・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-03",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "形式変換 — 文書・メディア変換継手（Tsugite）",
      "summary": "形式変換の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ形式変換の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。形式を変えるのは backend で、この継手は変換をしません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "互換のために外の変換 backend で文書の形式を変えるとき、偽装した入力や pin していない変換先（出力 profile）で送ってしまうこと、そして戻った bytes が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
      "use_when": [
        "文書の形式変換を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "入力が application/pdf・image/png・image/jpeg・text/plain・application/json のどれかで、先頭の magic bytes で型を確かめてから送りたいとき。",
        "変換先の出力 profile と params を pin したいとき。",
        "変換後の bytes が receipt の digest と一致したときだけ受け取りたいとき。"
      ],
      "do_not_use_when": [
        "docx など、一覧に無い形式を入力として送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません（カタログの一言の「docx→pdf」の docx は、この一覧に入っていません）。",
        "変換の前後で意味や見た目が同じことを確かめたいとき。変換の成功は意味の同一ではなく、見た目の忠実さも確かめません。",
        "変換エンジンそのものがほしいとき。この継手は変換をせず、実 backend も含みません。",
        "変換した結果を保管したいとき。保存は object store 継手（C-01）の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "形式変換そのものはしません。形式を変えるのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "変換エンジンの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-04",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "サムネイル/プレビュー生成 — 文書・メディア変換継手（Tsugite）",
      "summary": "サムネイル/プレビュー生成の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へサムネイル作成の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。サムネイルを作るのは backend で、この継手はサムネイルを作りません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "一覧の見た目に使うサムネイルやプレビューを外の変換 backend で作るとき、偽装した入力や大きすぎる入力を送ってしまうこと、そして戻った画像が要求したものか確かめないまま表示に回してしまうこと（すり替え）を防ぎます。",
      "use_when": [
        "一覧に出すサムネイルやプレビューの作成を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "出力 profile と params を pin して、決めた形でしか作らせたくないとき。",
        "入力の大きさに上限（max_input_bytes）を設け、超えるものは送りたくないとき。",
        "backend が返した画像の bytes が、receipt の digest と一致したときだけ受け取りたいとき。"
      ],
      "do_not_use_when": [
        "サムネイルを作る仕組みそのものがほしいとき。この継手はサムネイルを作らず、実 backend も含みません。",
        "サムネイルの見た目が元と合っているか（忠実さ）を確かめたいとき。確かめません。",
        "作ったサムネイルを保管したいとき。保存は object store 継手（C-01）の役目です。",
        "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "サムネイル・プレビューの作成そのものはしません。作るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "変換エンジンの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-05",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "画像最適化 — 文書・メディア変換継手（Tsugite）",
      "summary": "画像最適化の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ画像最適化の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。画像を縮小・軽量化するのは backend で、この継手は画像を変えません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "表示速度のために外の変換 backend で画像を縮小・軽量化するとき、偽装した入力や pin していない設定で送ってしまうこと、そして戻った画像が要求したものか確かめないまま使ってしまうこと（すり替え）を防ぎます。",
      "use_when": [
        "画像の縮小・軽量化を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "入力が image/png・image/jpeg などの許可した型で、先頭の magic bytes で型を確かめてから送りたいとき。",
        "最適化の出力 profile と params を pin したいとき。",
        "backend が返した画像の bytes が、receipt の digest と一致したときだけ受け取りたいとき。"
      ],
      "do_not_use_when": [
        "画像を縮小・軽量化する仕組みそのものがほしいとき。この継手は画像を変えず、実 backend も含みません。",
        "最適化した画像の見た目が元と合っているか（忠実さ）を確かめたいとき。確かめません。",
        "最適化した画像を保管したいとき。保存は object store 継手（C-01）の役目です。",
        "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "画像最適化そのものはしません。縮小・軽量化するのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "変換エンジンの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-06",
      "category": "J/文書・メディア処理",
      "kind": "Component",
      "title": "電子契約（envelope） — 電子契約（envelope）の部品（Component）",
      "summary": "電子契約（envelope）の部品（Component）は、署名フローを束ねる部品です。文書の digest を固定し（B-07）、決めた署名者それぞれの署名を pin した鍵で検め（B-02）、状態の遷移（開く・署名・完了・取り消し・時刻）を監査台帳の継手（H-01）に記録できたときだけ状態を進めます。完了の digest に付けた時刻トークンの検め（B-06）は任意で束ねます。台帳の結果が分からないときは状態を進めず保留にします（fail-closed）。署名が揃ったことは、法的な受け入れ・権限・承認ではありません。",
      "problem": "B 署名と別の層として、署名フローで、台帳に記録の無い状態を名乗ること、ほかの envelope・文書・署名者のための署名を流用されること、台帳の結果が分からないまま状態を進めてしまうこと、文書の本文を台帳へ渡してしまうことを防ぎます。",
      "use_when": [
        "1 つの文書に、決めた署名者（1〜20 人）の署名を集め、全員の署名が検めを通って記録されたときだけ COMPLETED にしたいとき。",
        "署名者ごとの署名を、使う側が固定した公開鍵（pinned_keys）で B-02 の部品に検めさせたいとき。署名者は signingBytes(signer_ref) が返す bytes（envelope_ref・文書の digest・signer_ref を含む）に署名します。",
        "状態の遷移を、H-01 の監査台帳の継手に AuditEventEnvelope として追記し、記録できた遷移（APPENDED_VERIFIED か ALREADY_APPENDED）だけを状態にしたいとき。台帳へ渡すのは遷移の payload の digest だけです。",
        "完了の後に、完了の digest に付けた時刻トークンを、B-06 の verifyToken を束ねた関数で 1 回だけ検めて記録したいとき（任意）。",
        "台帳の結果が分からなかった記録を、同じ封筒で送り直したいとき（resolvePending）。"
      ],
      "do_not_use_when": [
        "署名が揃ったこと・時刻が付いたことを、法的な受け入れ・権限・委任・承認として使いたいとき。「署名済み」は、pin した鍵の持ち主がこの envelope・この文書・この署名者の bytes に署名したことだけを示します。",
        "署名者の本人確認が要るとき。この部品は本人確認・権限・委任、署名の法的な効力を扱いません。",
        "署名の依頼の送付（通知）まで任せたいとき。通知は束ねません。送るなら呼び出し側が D-01 の継手で送ります。",
        "時刻トークンの取得（TSA への依頼）まで任せたいとき。取得は含まず、B-06 も検めだけです。",
        "外部の電子署名サービスへつなぎたいとき。その場合は別の継手の契約が要り、今は作りません。",
        "再起動をまたいでフローを続けたいとき。状態はこのプロセスの中だけで、台帳の記録から状態を組み立て直す機能は v1 にありません。",
        "同じ envelope_ref で 2 つのフローを作りたいとき。event_id が重なり、台帳が断ります（v1 は envelope_ref ごとに 1 つのフロー）。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違うとき（参照が不正、署名者や鍵の重複、pin に無い鍵、21 人以上、台帳の設定の余分なキー、getter）、INVALID_OPTIONS で止めます。",
        "その状態ではできない操作のとき（開く前・取り消しの後の署名、完了の後の取り消し、完了の前・2 回目の時刻）WRONG_STATE、前の操作が終わっていないとき BUSY で止めます。",
        "決めた署名者でないとき UNKNOWN_SIGNER、すでに署名したとき ALREADY_SIGNED、署名が B-02 の検めを通らないとき（ほかの envelope・文書・署名者の bytes、ほかの鍵）SIGNATURE_REJECTED で止めます。",
        "台帳の結果が分からないとき（UNKNOWN・例外・返事が無い）、状態を進めずに LEDGER_UNKNOWN で保留にし、保留の間は resolvePending のほかを RECORD_PENDING で止めます。保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。",
        "台帳が断ったとき（REFUSED・RECEIPT_REJECTED・HALTED など）や台帳の先頭の形が不正なとき、状態を進めず保留も残さずに LEDGER_REFUSED で止めます（先頭が不正なら送りません）。",
        "時刻の依頼が完了の digest でない・hash_algo が \"SHA256\" でない・B-06 の検めを通らないとき TIMESTAMP_REJECTED、verifyTimestamp の port が無いとき NOT_CONFIGURED で止めます。",
        "台帳の state() や検めの port が例外を投げたとき PORT_FAILED、port の結果の getter が投げたとき INSPECTION_FAILED、contentAddress の結果が不正なとき DIGEST_FAILED、timestamp が不正なとき CLOCK_FAILED で止めます。"
      ],
      "what_it_does_not_do": [
        "電子契約（envelope）の部品は継手ではありません。自分の Request/Receipt は無く、台帳への記録という外への効果は、束ねた H-01 の継手が自分の契約（audit-ledger/v1）で起こします。この部品自身は外へ触れません。",
        "束ねた部品の意味（digest の同一性・pin した鍵での検め・台帳の受領の照合・時刻トークンの検め、どれも承認ではない）は、それぞれの部品の側に残ります。",
        "署名が揃ったこと・時刻が付いたことは、法的な受け入れ・権限・委任・承認ではありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
        "署名者の本人確認・権限・委任、署名の法的な効力は扱いません。",
        "通知（署名の依頼の送付）と、時刻トークンの取得（TSA への依頼）は含みません。",
        "本物の台帳・TSA には一度も接続していません。単体試験とファズは代役で、署名の検証も代役です（暗号の正しさは B-02 の役目）。本物の 4 部品のコードとの結線は、手元の代役の backend を相手に確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の電子署名サービスの使い方の雛形ではありません。"
      ],
      "integration": "src/envelope.js の createEnvelopeFlow(options) で作ります。受け取るのは envelope_ref・signers・pinned_keys・ledger・ports などで、{ ok, errors, instance } を返します。各操作は { ok, errors, approval: false, phase, signed, … } を返します。利用側が用意する port は contentAddress（B-07）・verifySignature（B-02）・ledger.append と state（H-01 の継手のクライアント）・任意の verifyTimestamp（B-06）です。副作用は束ねた継手が起こし（EXTERNAL_IRREVERSIBLE）、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-07",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "バーコード/QR 生成・読取 — 文書・メディア変換継手（Tsugite）",
      "summary": "バーコード/QR 生成・読取の項目のうち、文書・メディア変換継手（Tsugite）が扱うのは QR の読取（QR_DECODE）の要求だけです。外の変換 backend へ画像を渡す前に入力と操作を検め、戻った receipt と出力を照合する継手（つぎて）です。QR を読むのは backend で、この継手は読みません。QR の生成は契約に無く、読めた内容を検証済みの事実とも信頼とも扱いません（fail-closed）。",
      "problem": "現場の入出力で QR を読むとき、偽装した画像を送ってしまうこと、抽出の結果（extracted_ref）が無いのに成功と扱ってしまうこと、読めた内容を確かめた事実のように扱ってしまうことを防ぎます。",
      "use_when": [
        "QR の読取を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "QR_DECODE の成功には extracted_ref が必ず付いていてほしいとき。付いていない成功は受理しません。",
        "読めた内容を受け取り、事実として扱うかの判断は核（evidence 系）に任せたいとき。",
        "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "QR を生成したいとき。QR の生成は契約に無く、この継手が扱うのは読取（QR_DECODE）だけです。",
        "バーコードを扱いたいとき。この部品の操作に挙がっているのは QR 読取で、バーコードは挙がっていません。",
        "読めた内容が正しいことを確かめたいとき。QR で読めた内容の正しさは確かめず、検証済みの事実とも信頼とも扱いません。",
        "QR を読む仕組みそのものがほしいとき。この継手は読取をせず、実 backend も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "QR の生成はしません。契約に無く、扱うのは QR_DECODE の読取の要求だけです。",
        "QR の読取そのものもしません。読むのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "QR で読めた内容の正しさは確かめません（契約の const のとおり、主張しません）。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED（QR では extracted_ref 付き）・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-08",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "透かし・スタンプ — 文書・メディア変換継手（Tsugite）",
      "summary": "透かし・スタンプの裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ透かしの要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。透かしを入れるのは backend で、この継手は透かしを入れません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "出所・秘匿の刻印として外の変換 backend で透かしを入れるとき、偽装した入力や pin していない設定で送ってしまうこと、そして戻った出力が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
      "use_when": [
        "文書や画像への透かしを外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "透かしの出力 profile と params を pin して、決めた形でしか入れさせたくないとき。",
        "backend が返した出力の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
        "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "電子契約が要るとき。電子契約（J-06）は作っておらず、この部品は J-06 を担いません。電子契約の操作を求める要求は送りません。",
        "何を刻印するか・何を秘匿するかを決めたいとき。決定は核（redact-facts）と人の役目で、この継手は送る前と受け取った後の検めだけです。",
        "透かしの入った出力を、信頼や開示の許可の根拠にしたいとき。どの結果も trust: NOT_DERIVED で、開示の許可も導きません。",
        "透かしを入れる仕組みそのものがほしいとき。この継手は透かしを入れず、実 backend も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "透かしを入れることそのものはしません。入れるのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "電子契約はしません。電子契約（J-06）は作っていません。",
        "変換の見た目の忠実さ（透かしが見たとおりに入っているか）は確かめません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "J-09",
      "category": "J/文書・メディア処理",
      "kind": "Tsugite",
      "title": "秘匿レンダリング — 文書・メディア変換継手（Tsugite）",
      "summary": "秘匿レンダリングの裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ黒塗りの描画の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。黒塗りを描くのは backend で、何を黒塗りにするかを決めるのは核（redact-facts）と人です。この継手は描画の要求を渡して照合するだけで、開示を許可しません（fail-closed）。",
      "problem": "核（redact-facts）が決めた黒塗りを外の変換 backend で描画させるとき、偽装した入力を送ってしまうこと、「開示の許可あり」と名乗る receipt を受け取ってしまうこと、戻った出力が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
      "use_when": [
        "核（redact-facts）と人が決めた黒塗りの描画を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
        "描画の結果を開示の許可と取り違えたくないとき。disclosure_authorization が NOT_DERIVED 以外の receipt は受理しません。",
        "backend が返した出力の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
        "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "何を黒塗りにするかを決めたいとき。黒塗りの対象の決定は核と人の役目で、この継手は描画の要求を渡すだけです。",
        "黒塗りした出力を開示してよいかを判断したいとき。黒塗りの描画は開示の許可ではありません。",
        "黒塗りが見たとおりに描かれているか（忠実さ）を確かめたいとき。確かめません。",
        "黒塗りを描く仕組みそのものがほしいとき。この継手は描画をせず、実 backend も含みません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
        "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
        "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
        "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
        "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
        "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
        "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "黒塗りの描画そのものはしません。描くのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
        "黒塗りの対象を決めず、開示を許可しません。対象の決定は核（redact-facts）と人の役目です。",
        "変換の見た目の忠実さは確かめません。",
        "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
        "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
        "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-01",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "構造化ログ — 可観測性 sink 継手（Tsugite）",
      "summary": "可観測性 sink 継手（Tsugite）は、機械可読の構造化ログ（ts・level・event・attrs の記録）の batch を、許可したイベントとキー・型・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。受け付けられなかった記録は欠落として返します。",
      "problem": "障害調査のためにログを外部の sink へ流すとき、メールアドレスや資格情報のような機微な値や自由文がログに混じって sink へ複製されること、そして sink が受け付けなかった記録が黙って消えることを防ぎます。許可したキーの短い基本型だけを送り、欠落は SUNK_PARTIAL と dropped で返します。",
      "use_when": [
        "機械可読の記録として、ts・level・event・attrs の形の構造化ログを外部の sink へ送りたいとき。",
        "ログに載せてよいイベント（log_events）とキー（log_attrs）を決めておき、それ以外を送らないようにしたいとき。",
        "ログから機微な値を sink へ複製したくないとき。最小化の記録（redaction_manifest_ref）を必須にできます。",
        "sink が受け付けなかったログを、黙って捨てずに欠落の事実として受け取りたいとき。",
        "同じ batch を二重に流したくないとき。同じ request_id は ALREADY_SUNK として送りません。"
      ],
      "do_not_use_when": [
        "自由文のメッセージや長い本文をそのままログに残したいとき。自由文の欄はなく、許可キーの短い基本型だけを送ります。",
        "エラー追跡（K-04）やアラート（K-06）が要るとき。別の部品で、この継手には含みません。",
        "すぐに使える sink がほしいとき。実 sink（各社の log の受け口）は含まず、まだ決まっていません。",
        "sink の受付の数を、ログの内容が正しいことの検証として扱いたいとき。観測は検証済みの事実ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "機微らしい値を検めます。メールアドレス、10 桁以上の数字の並び、Bearer、JWT、鍵、資格情報らしい記述が含まれていれば送りません（REFUSED）。",
        "自由文の欄、許可していないイベントやキー（attrs）を含む記録は送りません。",
        "大きさを検めます。batch の大きさ（max_payload_bytes）、記録の数（max_records）、値の長さ（max_attr_len）の上限を超えるものは送りません。",
        "pin した sink を検めます。pin していない sink へは送りません。payload の改ざん（payload_digest の不一致）があれば送りません。",
        "最小化の記録（redaction_manifest_ref）を必須とし、無いものは送りません。これは契約より厳しくした点です。",
        "受領（SinkReceipt）を照合します。request の digest を偽った受領や、送った数より多い受付は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。受領の observation_state が UNKNOWN なら照合しません。"
      ],
      "what_it_does_not_do": [
        "ログの構造を決めるのは核（policy-facts）、最小化を決めるのは核（redact-facts）の役目です。この継手がするのは sink の手前での検めだけです。",
        "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可キーと値の短さで抑えているだけで、見分けてはいません。",
        "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
        "この部品は K-01 のほか、K-02（メトリクス）・K-03（分散トレース）も同じ 1 つの部品として担い、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "監視サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に log の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・許可イベントとキー・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-02",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "メトリクス — 可観測性 sink 継手（Tsugite）",
      "summary": "可観測性 sink 継手（Tsugite）は、数値の常時計測であるメトリクス（ts・name・type・value・labels の記録）の batch を、許可した metric とラベル・型・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）の受付の数を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。受け付けられなかった記録は欠落として返します。",
      "problem": "メトリクスを外部の sink へ流し続けるとき、決めていない metric やラベルが増えたり、ラベルに機微な値が混じって sink へ複製されたりすること、そして sink が受け付けなかった数値が黙って消えることを防ぎます。送った数より多い受付のような受領の異常では止まります。",
      "use_when": [
        "数値の常時計測として、ts・name・type・value・labels の形のメトリクスを外部の sink へ送りたいとき。",
        "送ってよい metric（metrics）とラベル（metric_labels）を決めておき、それ以外を送らないようにしたいとき。",
        "sink が受け付けなかった数値を、黙って捨てずに欠落の事実（SUNK_PARTIAL と dropped）として受け取りたいとき。",
        "送った数より多い受付を返す受領を異常として扱い、以後の送信を止めたいとき。",
        "同じ batch を二重に流したくないとき。同じ request_id は ALREADY_SUNK として送りません。"
      ],
      "do_not_use_when": [
        "メトリクスの閾値を判定したり、アラートを出したりしたいとき。閾値の事実は核（monitor-detect）の役目で、アラート（K-06）は別の部品です。",
        "ラベルに自由文や長い値を載せたいとき。自由文の欄はなく、許可ラベルの短い基本型だけを送ります。",
        "すぐに使える sink がほしいとき。実 sink（各社の metrics の受け口）は含まず、まだ決まっていません。",
        "sink の受付の数を、計測値が正しいことの検証として扱いたいとき。観測は検証済みの事実ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "許可していない metric やラベル、自由文の欄を含む記録は送りません（REFUSED）。",
        "機微らしい値を検めます。メールアドレス、10 桁以上の数字の並び、Bearer、JWT、鍵、資格情報らしい記述が含まれていれば送りません。",
        "大きさを検めます。batch の大きさ（max_payload_bytes）、記録の数（max_records）、値の長さ（max_attr_len）の上限を超えるものは送りません。",
        "pin した sink を検めます。pin していない sink へは送りません。payload の改ざん（payload_digest の不一致）があれば送りません。",
        "受領（SinkReceipt）の受付の数を照合します。送った数より多い受付や、request の digest を偽った受領は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "最小化の記録（redaction_manifest_ref）を必須とし、無いものは送りません。これは契約より厳しくした点です。",
        "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。受領の observation_state が UNKNOWN なら照合しません。"
      ],
      "what_it_does_not_do": [
        "閾値の判定はしません。metrics の閾値の事実は核（monitor-detect）、最小化の決定は核（redact-facts）の役目で、この継手は sink の手前で検めるだけです。",
        "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可ラベルと値の短さで抑えているだけで、見分けてはいません。",
        "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
        "K-02 専用の部品ではありません。K-01（構造化ログ）・K-03（分散トレース）と同じ 1 つの部品で、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "監視サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に metrics の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・許可 metric とラベル・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-03",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "分散トレース — 可観測性 sink 継手（Tsugite）",
      "summary": "可観測性 sink 継手（Tsugite）は、処理の経路追跡に使う trace（trace_id・span_id・parent_span_id・name・start_ts・end_ts・attrs の span）の batch を、span の相関（同じ trace・根の span が 1 つ・親が同じ batch の中）、許可キー・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "遅延の原因を追うために trace を外部の sink へ流すとき、別の trace の span が混じる、親の見つからない span がある、根が複数ある、といった相関の壊れた trace や、途中の断片だけの trace を流してしまうことを防ぎます。span の attrs に機微な値が混じって sink へ複製されることも拒否します。",
      "use_when": [
        "処理の経路追跡として、trace_id・span_id・parent_span_id・name・start_ts・end_ts・attrs の形の span を外部の sink へ送りたいとき。",
        "1 つの trace をまとまった形（根の span がちょうど 1 つで、親がすべて同じ batch の中にある形）でだけ送りたいとき。",
        "trace ごとに相関の参照（correlation_ref）を必ず付けて送りたいとき。trace では correlation_ref を必須としています。",
        "span に載せてよいキー（span_attrs）を決めておき、それ以外を送らないようにしたいとき。",
        "sink が受け付けなかった span を、黙って捨てずに欠落の事実（SUNK_PARTIAL と dropped）として受け取りたいとき。"
      ],
      "do_not_use_when": [
        "trace の途中の断片だけを送りたいとき。根の span がちょうど 1 つで、親がすべて同じ batch の中にある batch だけを送ります。",
        "trace の連結が正しいかの最終の判断を、この継手に任せたいとき。trace の連結は核（provenance-facts）の役目です。",
        "エラー追跡（K-04）やアラート（K-06）が要るとき。別の部品で、この継手には含みません。",
        "すぐに使える sink がほしいとき。実 sink（各社の trace の受け口）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "trace の相関を検めます。別の trace の span が混じる、終了が開始より前の span があるときは送りません（REFUSED）。",
        "根の span の数と親の場所を検めます。根が複数ある batch、親が batch の外にある span（orphan）を含む batch は送りません。",
        "trace に相関の参照（correlation_ref）と最小化の記録（redaction_manifest_ref）が無いものは送りません。どちらも契約より厳しくした点です。",
        "span の attrs を検めます。許可していないキー、自由文の欄、メールアドレス・10 桁以上の数字の並び・Bearer・JWT・鍵・資格情報らしい記述を含むものは送りません。",
        "大きさと宛先を検めます。batch の大きさ・記録の数・値の長さの上限を超えるもの、pin していない sink 宛て、payload の改ざん（payload_digest の不一致）があるものは送りません。",
        "受領（SinkReceipt）を照合します。request の digest を偽った受領や、送った数より多い受付は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。同じ request_id は二重に流しません（ALREADY_SUNK）。"
      ],
      "what_it_does_not_do": [
        "trace の連結を決めるのは核（provenance-facts）、最小化を決めるのは核（redact-facts）の役目です。この継手がするのは sink の手前での検めだけです。",
        "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可キーと値の短さで抑えているだけで、見分けてはいません。",
        "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
        "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
        "K-03 専用の部品ではありません。K-01（構造化ログ）・K-02（メトリクス）と同じ 1 つの部品で、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "監視サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に span の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・span の許可キー・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-04",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "エラー追跡 — 可観測の拡張継手（Tsugite）",
      "summary": "可観測の拡張継手（Tsugite）は、K-04 エラー追跡としては、エラー追跡の要求（ErrorTrackRequest）を、pin した送り先と、解決できる失敗の参照（failure_ref）で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "エラーの記録を外部のエラー追跡の基盤へ送るとき、決めていない送り先へ送ってしまうこと、解決できない失敗の参照を送ってしまうこと、そして基盤の receipt を照合しないまま「記録できた」と扱ってしまうことを防ぎます。記録できたことを、検証済みの事実として扱わないための検めです。",
      "use_when": [
        "「たまに落ちる」のような失敗の記録を、エラー追跡の基盤へ送りたいとき。",
        "送ってよいエラー追跡の送り先（sink_refs）を決めておき、それ以外へ送らないようにしたいとき。",
        "失敗の参照（failure_ref）を、ai-execution-error の記録へ解決できるときだけ送りたいとき。",
        "同じエラーの記録を二重に送りたくないとき。同じ request_id は二度送らず、再送は照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "例外そのものを集めたいとき。この継手はエラー追跡の要求を運ぶ継手で、例外を集める機能は含みません。",
        "記録できたことを、検証済みの事実や承認として扱いたいとき。DONE の結果は verified_fact: NOT_ASSERTED で、approval: false です。",
        "failure_ref が指す記録の中身まで検めたいとき。この継手が確かめるのは、解決できるかどうかだけです。",
        "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していない送り先への要求は送りません。",
        "failure_ref を載せた要求は、port の resolveFailureRef で ai-execution-error の記録へ解決できないとき、または解決の手段が無いとき、送りません（REFUSED）。",
        "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
        "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
        "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "例外を集める機能は含みません。エラー追跡の要求を検めて基盤へ運び、receipt を照合するだけです。",
        "failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけ）。",
        "記録できたことを、検証済みの事実とはしません（verified_fact: NOT_ASSERTED）。",
        "記録できたことは承認ではありません（approval: false）。",
        "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: track_state・observation_state・verified_fact: NOT_ASSERTED。利用側の用意: cfg（pin する sink_refs ほか）と port（submit・任意で lookup・failure_ref 用に resolveFailureRef）。副作用は最大 EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-05",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "ヘルスチェック — 可観測の拡張継手（Tsugite）",
      "summary": "可観測の拡張継手（Tsugite）は、K-05 ヘルスチェックとしては、ヘルスチェックの要求（HealthCheckRequest）を pin した対象で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。ヘルスの PASS を意味の正しさと取り違えず、一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "生死・準備状態を確かめるとき、決めていない対象へ問い合わせてしまうこと、そしてヘルスの PASS を「中身も正しい」と読み違えることを防ぎます。意味の正しさを名乗る receipt は受理しません。",
      "use_when": [
        "生死・準備状態のヘルスチェックの要求を、基盤へ運びたいとき。",
        "問い合わせてよい対象（health_targets）を決めておき、それ以外を問い合わせないようにしたいとき。",
        "ヘルスの結果（health_state）と、意味の正しさ（semantic_correctness）を分けて受け取りたいとき。"
      ],
      "do_not_use_when": [
        "ヘルスの PASS を、処理結果の意味の正しさの証明として使いたいとき。この継手は PASS から意味の正しさを導きません。",
        "異常を見つけて自動で復旧させたいとき。この継手はヘルスチェックの要求を運ぶ継手で、復旧の機能は含みません。",
        "閾値で異常を判定したいとき。閾値と検出の事実は核（monitor-detect）の役目です。",
        "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していない対象へのヘルスチェックの要求は送りません。",
        "意味の正しさを主張するヘルスの receipt は受理しません。",
        "ヘルスチェックの効果は NONE です。外への効果を名乗る receipt は、効果を増やすものとして受理しません。",
        "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
        "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
        "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "ヘルスの PASS から、意味の正しさを導きません。",
        "復旧の機能は含みません。ヘルスチェックの要求を検めて基盤へ運び、receipt を照合するだけです。",
        "ヘルスの結果は承認ではありません（approval: false）。",
        "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: health_state・semantic_correctness。利用側の用意: cfg（health_targets などの pin する一覧）と port（submit、任意の lookup）。ヘルスチェックの副作用は NONE です。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-06",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "アラート・オンコール — 可観測の拡張継手（Tsugite）",
      "summary": "可観測の拡張継手（Tsugite）は、K-06 アラート・オンコールとしては、アラートの要求（AlertRequest）を pin したアラートと宛先で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。届けたアラートと受け取られたアラートを分けて返し、一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "異常を通報するとき、決めていない宛先や任意の URL へアラートを送ってしまうこと、そして「届けた」を「受け取られた」と読み違えることを防ぎます。",
      "use_when": [
        "異常の通報（アラート）の要求を、基盤へ運びたいとき。",
        "アラート（alert_refs）と宛先（alert_destinations）を pin し、任意の URL へ送らないようにしたいとき。",
        "届けた（delivered）と受け取られた（acknowledged）を、分けて受け取りたいとき。"
      ],
      "do_not_use_when": [
        "アラートが実際に人に届いたか・対応されたかを確かめたいとき。この継手は receipt の状態を返すだけで、確かめません。",
        "オンコールの当番を組んだり、呼び出しを段階的に広げたりしたいとき。この継手はアラートの要求を運ぶ継手で、それらの機能は含みません。",
        "異常を閾値で判定したいとき。閾値と検出の事実は核（monitor-detect）、アラートの方針は核（policy-facts）の役目です。",
        "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していないアラート、pin していない宛先への要求は送りません。宛先に任意の URL を差し込む要求（destination injection）も送りません。",
        "delivered は DELIVERED か ACKNOWLEDGED のときだけ、acknowledged は ACKNOWLEDGED のときだけ認めます。外への効果を伴わずに届けたと名乗る receipt は受理しません。",
        "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
        "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
        "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "アラートが実際に人に届いたか・対応されたかは確かめません。receipt の状態を返すだけです。",
        "オンコールの当番や呼び出しの機能は含みません。アラートの要求を検めて基盤へ運び、receipt を照合するだけです。",
        "アラートを届けたことは承認ではありません（approval: false）。",
        "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: alert_state・delivered・acknowledged。利用側の用意: pin する一覧（alert_refs・alert_destinations など）の cfg と、port（submit・任意の lookup）。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-07",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "フィーチャーフラグ — 可観測の拡張継手（Tsugite）",
      "summary": "可観測の拡張継手（Tsugite）は、K-07 フィーチャーフラグとしては、機能フラグの要求（FeatureFlagRequest、READ・SET・CLEAR）を pin したフラグと値で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。フラグの状態から許可を導かず、一つでも合わなければ送らず、受理しません（fail-closed）。",
      "problem": "機能の出し分けのために機能フラグを読み書きするとき、決めていないフラグや値を立ててしまうこと、そしてフラグが立っていることを「許可された」と読み違えることを防ぎます。許可を名乗るフラグの receipt は受理しません。",
      "use_when": [
        "機能フラグの READ・SET・CLEAR の要求を、基盤へ運びたいとき。",
        "扱ってよいフラグ（flag_refs）と値（flag_value_refs）を決めておき、それ以外を扱わないようにしたいとき。",
        "フラグの状態（flag_state）と許可（authorization）を分けて受け取りたいとき。"
      ],
      "do_not_use_when": [
        "フラグの状態を、利用者や機能の許可として使いたいとき。この継手はフラグの状態から許可を導きません。",
        "READ でフラグの値そのものを受け取りたいとき。値は receipt に無く、この継手は状態だけを返します。",
        "段階公開の割合や対象を決めたいとき。この継手は機能フラグの要求を運ぶ継手で、出し分けを決める機能は含みません。フラグの方針は核（policy-facts）の役目です。",
        "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していないフラグ・pin していない値の要求は送りません。",
        "value_ref は SET のときだけ必須で、READ に値を載せた要求は送りません。op と効果が合わない要求（READ は NONE、SET・CLEAR は EXTERNAL_REVERSIBLE）も送りません。",
        "状態が op に対応しない receipt（READ→READ・SET→SET・CLEAR→CLEARED か FAILED 以外）は受理しません。",
        "許可を名乗るフラグの receipt は受理しません。",
        "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
        "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
        "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "フラグの状態から許可を導きません。",
        "フラグの値そのもの（READ で何が返るか）は返しません。状態だけを返します。",
        "出し分けを決める機能は含みません。機能フラグの要求を検めて基盤へ運び、receipt を照合するだけです。",
        "フラグを立てたことは承認ではありません（approval: false）。",
        "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: flag_state・authorization。利用側の用意: pin する一覧（flag_refs・flag_value_refs など）の cfg と、port（submit・任意の lookup）。副作用は READ が NONE、SET・CLEAR が EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "K-08",
      "category": "K/可観測性・運用",
      "kind": "Tsugite",
      "title": "使用量メータ — 可観測の拡張継手（Tsugite）",
      "summary": "可観測の拡張継手（Tsugite）は、K-08 使用量メータとしては、使用量のイベントを記録する要求（UsageMeterRequest）を、pin した計測器と単位、量の上限で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。課金はせず、量の正しさも確かめません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
      "problem": "使用量を外部の計測の基盤へ記録するとき、決めていない計測器や単位で記録してしまうこと、小数・負・上限を超える量を送ってしまうこと、そして記録した量を検証済みの業務の事実と読み違えることを防ぎます。",
      "use_when": [
        "使用量のイベントを、計測の基盤へ運びたいとき。",
        "記録してよい計測器（meter_refs）と単位（unit_refs）、量の上限（max_quantity）を決めておき、それ以外を送らないようにしたいとき。",
        "同じ使用量を二重に記録したくないとき。同じ冪等キーで別の量は送らず、同じ request_id は二度送りません。"
      ],
      "do_not_use_when": [
        "課金をしたいとき。この継手は使用量のイベントを運ぶ継手で、課金の機能は含みません。",
        "記録した量を、そのまま課金に使いたいとき。量の正しさ（課金に使えるか）は確かめず、検証済みの業務の事実ではありません。",
        "小数の量を記録したいとき。量は 0 以上の整数だけで、小数の量は digest を作れないため送りません。",
        "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "pin していない計測器・pin していない単位の要求は送りません。",
        "量が小数のとき、負のとき、上限（max_quantity）を超えるときは送りません。max_quantity は使用量を記録するとき必須です。",
        "量を検証済みの業務の事実と主張する receipt は受理しません。",
        "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
        "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
        "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
        "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "課金はしません。使用量のイベントを検めて基盤へ運び、receipt を照合するだけです。",
        "量の正しさ（課金に使えるか）は確かめません。記録した量は検証済みの業務の事実ではありません。",
        "小数の量は扱いません（契約は number ですが、この継手は 0 以上の整数に締めています）。",
        "使用量を記録できたことは承認ではありません（approval: false）。",
        "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: meter_state・business_fact_verification。利用側の用意: pin する一覧（meter_refs・unit_refs など）と max_quantity の cfg、port（submit・任意の lookup）。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-01",
      "category": "L/地域化・整形",
      "kind": "Primitive",
      "title": "タイムゾーン変換 — タイムゾーン変換の部品（Primitive）",
      "summary": "タイムゾーン変換の部品（Primitive）は、epoch ミリ秒（安全な整数）と IANA の zone の壁時計（年月日・時分秒・ミリ秒）を行き来する単独の部品です。zone の規則は Node.js の組み込みの Intl（ICU の tz データ）に従い、結果にはいつも tz データと ICU の版を添えます。存在しない壁時計・二度ある壁時計を黙って直さず、{ ok, errors } を返して例外を投げません（fail-closed）。",
      "problem": "「時間がずれる」の裏で、夏時間の飛びで存在しない時刻や、重なりで二度ある時刻を、ほかの時刻に黙って寄せてしまうこと、2 月 30 日・24 時・60 秒のような欄を繰り上げて受け取ってしまうことを防ぎます。結果に tz データと ICU の版を添えるので、どの規則で出した答えかを後から確かめられます。",
      "use_when": [
        "epoch ミリ秒の瞬間を、IANA の zone（例: Asia/Tokyo）の壁時計と UTC からのずれに変えたいとき（toZoned）。",
        "zone の壁時計を epoch ミリ秒の瞬間に変えたいとき（fromZoned）。二度ある壁時計は既定で止め、earlier・later を明示したときだけ早い方・遅い方を選びます。",
        "二度ある壁時計を止めたときに、早い方・遅い方の瞬間の候補（candidates_ms）を受け取り、どちらにするかを使う側で決めたいとき。",
        "どの tz データ・ICU の版で出した答えかを、結果といっしょに残したいとき（tzdata の tz・icu・matches_pin）。",
        "例外を投げない変換が要るとき。toZoned と fromZoned はどちらも例外を投げず、入力を書き換えません。",
        "現在時刻・乱数・環境の locale を読まず、外の境界を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
      ],
      "do_not_use_when": [
        "tz データや ICU の版にかかわらず同じ答えが要るとき。答えはこの実行環境の tz データ（2026c）と ICU（78.3）に依り、版が変われば同じ入力でも答えが変わりえます。ほかの版では確かめていません（tzdata.matches_pin が false になります）。",
        "1970 年より前、2100 年より後の時刻を扱うとき。OUT_OF_RANGE で止めます。",
        "閏秒を扱いたいとき。瞬間は閏秒を数えない POSIX の時刻で、60 秒は INVALID_DATE で止めます。",
        "+09:00 のような UTC からのずれの書き方で zone を渡したいとき。zone は IANA の名前の形だけで、ほかは INVALID_ZONE で止めます。",
        "存在しない壁時計を、近い時刻に寄せてほしいとき。NONEXISTENT_LOCAL_TIME で止め、disambiguation に関わらずほかの時刻に寄せません。",
        "Node.js v24.21.0（tz データ 2026c・ICU 78.3）のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "瞬間が安全な整数でないとき（小数・NaN・Infinity・文字列・Date・bigint）INVALID_INSTANT、瞬間や年が 1970〜2100 の外のとき OUT_OF_RANGE で止めます。",
        "zone が文字列でない・64 文字を超える・IANA の名前の形でないとき（ずれの書き方・空白・空の区切り）INVALID_ZONE、形は合うがこの実行環境の Intl が知らないとき UNKNOWN_ZONE で止めます。",
        "壁時計が素のオブジェクトでない、必須の欄が無い、ほかのキー・getter・列挙しないプロパティ・symbol のキーがある、値が整数でないとき、INVALID_LOCAL で止めます。getter は呼びません。",
        "欄の値が範囲の外のとき（2 月 30 日・平年や 2100 年の 2 月 29 日・4 月 31 日・13 月・24 時・60 分・60 秒・1000 ミリ秒・負の値）、繰り上げずに INVALID_DATE で止めます。",
        "夏時間の飛びで存在しない壁時計のとき NONEXISTENT_LOCAL_TIME、重なりで二度ある壁時計のとき（disambiguation が reject（既定））AMBIGUOUS_LOCAL_TIME で止め、候補を添えます。",
        "options が素のオブジェクトでない、ほかのキー・getter がある、disambiguation が reject・earlier・later のどれでもないとき INVALID_OPTIONS、調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
        "壁時計が範囲の中でも、変換した瞬間が範囲の外に出るとき（例: Asia/Tokyo の 1970-01-01 08:59）、OUT_OF_RANGE で止めます。"
      ],
      "what_it_does_not_do": [
        "tz データを自分では持ちません。変換の規則は Node.js の ICU のもので、答えの正しさはこの実行環境の tz データが正しいときだけです。将来の時刻は、その tz データが作られた時点の規則の見込みで、規則の変更（夏時間の廃止など）はその版の tz データが出るまで反映されません。",
        "答えを独立に突き合わせたのは UTC・Asia/Tokyo（1970〜2100 年）と America/New_York・Europe/London（2010〜2035 年）だけです。ほかの zone は往復と性質の検めだけで、答えを別に確かめてはいません。",
        "fromZoned は前後 15 時間を 3 時間ごとに見たずれから候補を作ります。3 時間より短い間隔で変わり目が続く zone・時期では、候補を取りこぼすことがあります（1970〜2100 年の tz データで起きるかは全部は確かめていません）。",
        "何時を締め切りにするか・どの zone を使うか・営業日などの意味の判断はしません。使う側（Kernel・業務）に残ります。",
        "Temporal や外のライブラリとの答えの互換は確かめていません。Proxy の罠は、この部品が調べる操作（getPrototypeOf・ownKeys・getOwnPropertyDescriptor）のものだけを確かめました。",
        "変換できたことは、その時刻が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の日付ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/timezone.js の toZoned(instantMs, zone)・fromZoned(local, zone, options) を呼びます（投げません）。受け取るのは epoch ミリ秒か壁時計、IANA の zone の名前、任意の { disambiguation } です。返すのは { ok: true, errors: [], local か instant_ms, offset_seconds, zone, resolved_zone, tzdata, … } か { ok: false, errors: [{ code }], tzdata }（code は error_taxonomy）。副作用（NONE）・外の接続・資格情報は無く、利用側が用意するものはありません。状態は書式の覚え置き（プロセスの中だけ）で、答えには影響しません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-02",
      "category": "L/地域化・整形",
      "kind": "Tsugite",
      "title": "機械翻訳の運搬（i18n） — 地域対応の運び継手（Tsugite）",
      "summary": "地域対応の運び継手（Tsugite）は、機械翻訳の運搬（i18n）の項目では、機械翻訳の要求（TranslationRequest）を、pin した基盤と locale（訳す前と後）で検めてから翻訳の基盤へ運び、戻った receipt（TranslationReceipt）を契約どおりに照合する継手（つぎて）です。本文は運ばず、参照だけを運びます。文言の切替やカタログの管理はしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "翻訳の基盤へ要求を運ぶとき、本文をそのまま送ってしまうこと、pin していない基盤や locale へ送ってしまうこと、そして機械翻訳を人が確かめた翻訳や正本と取り違えることを防ぎます。",
      "use_when": [
        "機械翻訳の要求を、本文ではなく参照だけで、pin した翻訳の基盤へ運びたいとき。",
        "訳す前と後の locale を pin した一覧に限り、訳す前と後が同じ locale の要求を送らないようにしたいとき。",
        "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には translation_state・output_ref・human_verification・authoritative_status が添えられます。",
        "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "画面の文言を言語ごとに切り替えたいとき、または多言語の文言カタログを管理したいとき。この継手は機械翻訳の要求を運ぶ継手で、文言の切替・カタログの管理の機能は含みません。",
        "機械翻訳の結果を、人が確かめた翻訳や正本として扱いたいとき。そう名乗る receipt は受理しません。",
        "訳の正しさを確かめたいとき、または訳した文言の公開を承認させたいとき。訳の確認と公開の承認は、外の判断と人・組織の役目です。",
        "時刻の変換・通貨や数の書式・単位の換算・並べ替えなど、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
      ],
      "what_it_checks": [
        "pin していない基盤・locale への要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
        "訳す前と後の locale が同じ要求は送りません。",
        "人が確かめた翻訳（human verification）や正本（authoritative）を名乗る receipt は受理しません。",
        "できた（PRODUCED）のに出力の参照（output_ref）が無い receipt、できなかった（FAILED・UNAVAILABLE）のに出力の参照や効果を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "文言の切替・多言語カタログの管理はしません。この継手は機械翻訳の要求を運ぶ継手で、その機能は含みません。",
        "機械翻訳は、人が確かめた翻訳でも正本でもありません。訳の正しさは確かめません。",
        "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
        "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に TranslationRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED・UNAVAILABLE は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-03",
      "category": "L/地域化・整形",
      "kind": "Primitive",
      "title": "通貨整形 — 通貨整形の部品（Primitive）",
      "summary": "通貨整形の部品（Primitive）は、通貨の最小単位で数えた安全な整数（amount_minor）を、部品の中に固定した通貨の表（7 通貨）と locale の表（ja-JP・en-US）で、決まった表記の文字列にする単独の部品です。表示の整形だけで、通貨の換算・丸め方の方針・支払いの判断はしません。受け取れない値は決まったコードで止め、例外を投げません（fail-closed）。",
      "problem": "表示の裏で、動かす環境の国際化 API（ICU）によって表記が変わってしまうこと、桁を捨てたり丸めたりして表記が元の整数に戻らなくなること、表に無い通貨・locale や余分なキー（丸め方などのつもりの指定）を黙って近いものに寄せたり無視したりすることを防ぎます。",
      "use_when": [
        "すでに通貨の最小単位にした安全な整数を、表の通貨（日本・米国の通貨と、EUR・GBP・KRW・CNY・BHD）と locale（ja-JP・en-US）で、記号と桁区切りの付いた表記の文字列にしたいとき（formatMinor）。",
        "動かす環境に左右されない表記が要るとき。時計・乱数・環境の locale・国際化 API（ICU）を使わず、表はこの部品のファイルの中に固定していて、同じ入力からはいつでも同じ text ができます。",
        "表記を最小単位に戻すと、受け取った整数の絶対値にちょうど戻ること（桁を捨てない・丸めない）が要るとき。",
        "受け取れない入力を、どの欄か（field）とコードで、当てはまるものをすべて返してほしいとき。",
        "例外を投げない整形が要るとき。formatMinor は例外を投げず、入力を書き換えず、getter を呼びません。"
      ],
      "do_not_use_when": [
        "小数を含む値や、まだ最小単位にしていない値を渡したいとき。この部品は整数だけを受け取り、丸めもしません。最小単位にするときの丸め方の方針・換算は、使う側か G 系の部品の役目です。",
        "表の 7 通貨と 2 locale のほかを扱いたいとき。UNKNOWN_CURRENCY・UNKNOWN_LOCALE で止め、ja-jp・en-GB・ja などに寄せません。",
        "CLDR・Intl.NumberFormat と同じ出力が要るとき。一致は主張しません。記号の選び方と書き方はこの部品の決まりで、日本の通貨の記号は半角の U+00A5（CLDR の ja の全角 U+FFE5 ではない）、BHD は ISO 4217 のコードと空白（U+0020）です。",
        "記号を数の後ろに置く書き方、ほかの桁区切り（ピリオド・空白・インドの 2 桁区切り）、会計の書き方（括弧の負の数）、全角の数字・記号が要るとき。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "入力がオブジェクトでないとき（null・配列・数・文字列など）、キーが getter・setter のとき、入力を調べること自体が例外になるとき（Proxy の罠など）、INVALID_INPUT で止めます。getter は呼びません。",
        "amount_minor・currency・locale のほかのキーを持つとき（列挙しないキー・symbol のキーを含む）、黙って無視せずに UNKNOWN_FIELD で止めます。継承したキーは読みません。",
        "amount_minor が無い、number でない（bigint・文字列）、整数でない、安全な整数の範囲の外、NaN・Infinity のとき、INVALID_AMOUNT で止めます。",
        "currency が無い、文字列でない、表のコードでないとき（小文字・前後の空白・__proto__ などの継承した名前を含む）、UNKNOWN_CURRENCY で止めます。",
        "locale が無い、文字列でない、表の locale でないとき、近い locale に寄せずに UNKNOWN_LOCALE で止めます。",
        "いくつもの欄が受け取れないとき、errors に amount_minor → currency → locale の順で、当てはまるものをすべて並べます（入力の形が違うとき・余分なキーのときは 1 件だけ）。"
      ],
      "what_it_does_not_do": [
        "通貨整形の部品は表示の整形だけで、通貨の換算・丸め・税・請求・支払いの判断は含みません。それらは G 系の部品や使う側に残ります。",
        "受け取った整数の意味（最小単位にしたときの丸め・通貨の正しさ）は確かめません。受け取った整数をそのまま書きます。",
        "表の 7 通貨と 2 locale のほかは扱いません。ja-JP と en-US の出力は同じで（桁区切りはコンマ・小数点はピリオド・3 桁ごと・記号は数の前）、locale ごとの違いは作りません。",
        "記号は Unicode の文字そのままで、正規化はしません。表は CLDR・各国の慣行の写しではなく、この部品の決まりです。表は凍結していて、使う側は書き換えられません。",
        "表記の文字列ができたことは、額が正しいことでも、請求・支払いをしてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の定義の tsugite-l-03 は検めの型だけで、整形の実装は無く、比べる相手はありません。この部品は整形の実装になる候補です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の国際化ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/currency.js の formatMinor({ amount_minor, currency, locale }) を呼びます（投げません）。受け取るのは 3 つのキーだけのオブジェクトで、amount_minor は最小単位の安全な整数、currency は表の ISO 4217 のコード、locale は ja-JP か en-US です。返すのは { ok: true, errors: [], text } か { ok: false, errors: [{ code, field }], text: null }（code は error_taxonomy）。text は、負のときだけの符号・表の記号・3 桁ごとに区切った整数部・最小単位の桁数の小数部の順です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-04",
      "category": "L/地域化・整形",
      "kind": "Primitive",
      "title": "単位変換 — 単位変換の部品（Primitive）",
      "summary": "単位変換の部品（Primitive）は、10 進の文字列の値を、定義どおりの正確な有理数の係数（BigInt の分子・分母）で換算し、小数 0〜18 桁に偶数丸め（half-even）した 10 進の文字列を返す単独の部品です。長さ・質量・圧力は比例、温度（°C・°F・K）は 1 次式で、浮動小数点を使いません。dB の系（dBm↔mW）は v1 にありません。受け取れない値は決まったコードで止め、例外を投げません（fail-closed）。",
      "problem": "換算の途中に浮動小数点の誤差が入ること（\"0.1\" が 1/10 ちょうどでなくなるなど）、次元の違う換算や絶対零度より低い温度を黙って計算してしまうこと、似た綴りの単位や余分なキー（精度などのつもりの指定）を黙って近いものに寄せたり無視したりすることを防ぎます。",
      "use_when": [
        "長さ（m・km・cm・mm・in・ft・mi）、質量（kg・g・lb・oz）、圧力（Pa・kPa・MPa・bar・psi）、温度（K・C・F）の同じ次元の中で、値を換算したいとき（convert）。",
        "換算の結果を、正確な有理数から小数 fraction_digits 桁に half-even で 1 回だけ丸めた値として受け取りたいとき（途中で丸めない）。",
        "係数の出どころが定義値であることが要るとき（in = 0.0254 m、lb = 0.45359237 kg、bar = 100000 Pa、K = °C + 273.15 など）。",
        "派生の値を注入するときの換算の規則として使いたいとき。",
        "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。convert は例外を投げず、入力を書き換えず、getter を呼びません。"
      ],
      "do_not_use_when": [
        "dB の系（dBm↔mW・dB など）を換算したいとき。カタログの一言は「dBm/MPa 等」ですが、dB の系は対数で結果が有理数にならないため v1 に無く、表に無い単位として UNKNOWN_UNIT で止めます。",
        "表の 19 単位のほかを使いたいとき。μm・hPa のような接頭語の組み合わせは自動では作りません。",
        "温度の差（Δ°C ↔ Δ°F）を換算したいとき。温度は絶対温度として換算し、絶対零度より低い値は止めます。",
        "有効数字・誤差（不確かさ）の伝わり方や、half-even のほかの丸めが要るとき。",
        "locale による書き方（小数点のコンマなど）や桁区切りの付いた出力が要るとき。出力は桁区切りの無い 10 進の文字列だけです。",
        "浮動小数点で換算するほかの実装と同じ結果が要るとき。丸めの境目の近くで結果が違うことがあります。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "入力がオブジェクトでないとき、キーが getter・setter のとき、調べること自体が例外になるとき（Proxy の罠など）INVALID_INPUT、5 つのキーのほかのキーを持つとき（列挙しないキー・symbol のキーを含む）UNKNOWN_FIELD で止めます。",
        "value が文字列でない、64 文字を超える、10 進の文法に合わないとき（指数・16 進・NaN・Infinity・+・空白・桁区切り・\"1.\"・\".5\"・先頭の余分な 0・ASCII でない数字）、MALFORMED_VALUE で止めます。",
        "from・to が表の単位でないとき（dBm・mW などの dB の系、\"°C\"・\"KM\"・\"kpa\" などの似た綴り、__proto__ などの継承した名前を含む）、UNKNOWN_UNIT で止めます。",
        "from と to の次元が違うとき、黙って計算せずに INCOMPATIBLE_UNITS で止めます。",
        "温度の値が絶対零度（0 K = -273.15 °C = -459.67 °F）より低いとき、BELOW_ABSOLUTE_ZERO で止めます。絶対零度ちょうどは受け取ります。",
        "fraction_digits が 0〜18 の整数でないとき INVALID_FRACTION_DIGITS、rounding が無いか \"half-even\" ちょうどでないとき UNSUPPORTED_ROUNDING で止めます。",
        "いくつもの欄が受け取れないとき、errors に value → from → to → fraction_digits → rounding の順で、当てはまるものをすべて並べます。"
      ],
      "what_it_does_not_do": [
        "単位変換の部品は、カタログの一言にある dBm を含む dB の系の換算は含みません。v1 は有理数で正確に書ける長さ・質量・圧力（MPa を含む）・温度だけです。",
        "どの値を換算してよいか・突き合わせ（reconcile）は決めません。使う側に残ります。",
        "係数の値は定義から写したもので、オラクルも同じ定義から作ったため、定義そのものの写し違いは両方に同じく入りえます。確かめたのは既知の答えの試験（1 in = 25.4 mm・1 lb = 453.59237 g・1 psi = 6894.757293168361 Pa など）の範囲だけです。",
        "単位の名前は ASCII の記号そのままです（\"C\" は摂氏で、クーロンではありません）。",
        "換算できたことは、元の値や単位の選び方が正しいことでも、その値で設計・判断してよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の定義の tsugite-l-04 は検めの型だけで、換算の実装は無く、比べる相手はありません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の単位換算ライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/unitconvert.js の convert({ value, from, to, fraction_digits, rounding }) を呼びます（投げません）。受け取るのは 5 つのキーだけのオブジェクトで、value は 64 文字までの 10 進の文字列、from・to は表の単位の名前、fraction_digits は 0〜18 の整数、rounding は \"half-even\" ちょうど（省略できない）です。返すのは { ok: true, errors: [], value } か { ok: false, errors: [{ code, field }], value: null }（code は error_taxonomy）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-05",
      "category": "L/地域化・整形",
      "kind": "Tsugite",
      "title": "住所候補の照会 — 地域対応の運び継手（Tsugite）",
      "summary": "地域対応の運び継手（Tsugite）は、住所候補の照会の項目では、住所の候補の照会の要求（AddressLookupRequest）を、pin した基盤と地域で検めてから住所の基盤へ運び、戻った receipt（AddressLookupReceipt）を契約どおりに照合する継手（つぎて）です。住所の文面は運ばず、参照だけを運びます。届く住所かの検証も、住んでいる証明もしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "住所の基盤へ照会を運ぶとき、住所の文面をそのまま送ってしまうこと、pin していない基盤や地域へ送ってしまうこと、そして見つかった住所の候補を、届く住所の確認や住んでいる証明と取り違えることを防ぎます。",
      "use_when": [
        "住所の候補の照会を、住所の文面ではなく参照（query_ref）だけで、pin した住所の基盤へ運びたいとき。",
        "照会する地域を、pin した一覧に限りたいとき。",
        "外への効果の無い照会（副作用 NONE）として運びたいとき。",
        "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には lookup_state・candidate_ref・deliverability・residence_proof が添えられます。",
        "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "届く住所かどうか（配送できるか）を検証したいとき。住所の候補は届く住所の確認ではなく、そう名乗る receipt は受理しません。",
        "本人がそこに住んでいることの証明がほしいとき。住所の候補は住んでいる証明ではありません。本人の住所の証明は、外の判断と人・組織の役目です。",
        "住所の表記ゆれをそろえる（正規化する）仕組みそのものがほしいとき。この継手は住所の候補の照会の要求を運ぶ継手で、正規化の機能は含みません。",
        "住所の文面を基盤へ送りたいとき。運ぶのは参照だけで、空白を含む参照は住所の文面とみなして送りません。",
        "電話番号の正規化など、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
      ],
      "what_it_checks": [
        "pin していない基盤・地域への要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
        "照会の参照（query_ref）に空白があれば、住所の文面とみなして送りません。",
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
        "届く住所の確認（deliverability）や住んでいる証明（residence proof）を名乗る receipt は受理しません。",
        "見つからない（NOT_FOUND）のに候補の参照（candidate_ref）を持つ receipt、できなかったのに出力の参照や効果を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "届く住所かの検証も、住んでいる証明もしません。住所の候補は、そのどちらでもありません。届く住所かの確認と本人の住所の証明は、外の判断と人・組織の役目です。",
        "住所の正規化（表記ゆれの統一）の機能は含みません。この継手は住所の候補の照会の要求を運ぶ継手です。",
        "住所の候補の正しさは確かめません。また、住所の文面の検出は空白の有無だけを見る網で、すべての文面を見分けるものではありません。守りの本体は、参照だけを運ぶ作りです。",
        "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
        "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に AddressLookupRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は NONE です（住所の照会）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-06",
      "category": "L/地域化・整形",
      "kind": "Primitive",
      "title": "電話番号正規化（E.164） — 電話番号正規化（E.164）の部品（Primitive）",
      "summary": "電話番号正規化（E.164）の部品（Primitive）は、電話番号の文字列を E.164（\"+\" 国番号＋国内の番号 NSN）にする単独の部品です。使う表は、この部品が持つ版つきの小さな表（erabazu-e164-metadata/2026-09-30）だけで、地域は JP（+81）・NANP（US/CA、+1）・GB（+44）の 3 つです。受け取れない値は決まったコードで止めます（fail-closed）。正規化できたことは、番号が存在する・つながる証明ではありません。",
      "problem": "SMS の裏で、宛先の番号の書き方のゆれ（空白・ハイフン・ドット・丸かっこ、国内の形と国際の形）を決まった形にそろえます。そのときに、表に無い国番号・地域を推し量ったり、内線やほかの国際の頭の番号を黙って外したりせず、決まったコードで止めます。",
      "use_when": [
        "JP・NANP（US/CA）・GB の電話番号の文字列を、E.164 の文字列と、表の地域・国番号・NSN に分けて受け取りたいとき（normalize）。",
        "国内の形（先頭が + でない）の番号を、default_region（JP・US・CA・GB）を渡して、国内の頭の番号（trunk）を外した E.164 にしたいとき。",
        "SMS の宛先の形の検めに合う文字列を作りたいとき。この部品の出力は、通知の継手の SMS の宛先の形の検め（/^\\+[1-9][0-9]{6,14}$/）にいつも合います（作業場で 1 回突き合わせたもので、部品の試験には入れていません）。",
        "どの版の表で正規化したかを結果といっしょに残したいとき。どの結果にも metadata_revision が付きます。",
        "例外を投げない呼び方が要るとき。normalize は例外を投げず、入力（options を含む）を書き換えず、options の getter を呼びません。"
      ],
      "do_not_use_when": [
        "番号が存在する・使われている・つながる・誰かのものであることを確かめたいとき。この部品は確かめません（existence_verified はいつも false）。確かめるには別の手段（SMS の確認コードなど）が要ります。",
        "番号計画の全体（割り当て済みの番号帯・市外局番の一覧・番号の種類）を検めたいとき。表の長さと頭の数字に合うだけの番号も受け取ります。",
        "JP・NANP・GB のほかの地域を扱うとき、US と CA を区別したいとき。region は表の見出し（NANP）で、回線のある国ではありません。",
        "内線付きの番号、ほかの国際の頭の番号（00・010・011）で始まる番号、全角の数字、語呂合わせの英字を変換してほしいとき。v1 では受け取りません。",
        "番号計画の改定を自動で追ってほしいとき。表の値は 2026-09-30 に作った保守的なものです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "text が文字列でないとき NOT_A_STRING、64 文字を超えるとき中身を読まずに INPUT_TOO_LONG で止めます。",
        "opts が素のオブジェクトでない、default_region のほかのキー・getter がある、調べること自体が例外になるとき（Proxy の罠など）INVALID_OPTIONS、default_region が JP・US・CA・GB のどれでもないとき UNKNOWN_REGION で止めます。",
        "内線の印（#・;・, または x・ext・extension）があるとき EXTENSION_NOT_SUPPORTED、ASCII の英字があるとき LETTERS_NOT_ALLOWED で、黙って外さずに止めます。",
        "受け取る文字のほかの文字があるとき（全角の数字・タブ・改行・ノーブレークスペース・先頭でない + ・2 つ目の + など）INVALID_CHARACTER、数字が 1 つも無いとき EMPTY_NUMBER で止めます。",
        "国際の形で国番号が 1・44・81 のどれでもないとき UNSUPPORTED_COUNTRY_CODE、国内の形なのに default_region が無いとき MISSING_REGION で止め、推し量りません。",
        "国内の形の頭がその地域の国際の頭の番号（JP の 010・00、GB の 00、NANP の 011）のとき INTERNATIONAL_PREFIX_NOT_SUPPORTED、JP・GB の国内の形の頭に 0 が無いとき MISSING_TRUNK_PREFIX で止めます。",
        "NSN の桁数が表の長さでないとき INVALID_LENGTH、決まった位置の数字が下限より小さいとき（国番号の後の trunk の 0、NANP の市外局番・局番の頭の 0・1）INVALID_NUMBER_PATTERN で止めます。",
        "止めるときのエラーはいつも 1 つで、検める順番は決まっています（error_taxonomy の並びの順）。"
      ],
      "what_it_does_not_do": [
        "電話番号正規化（E.164）の部品は、宛先の文字列を決まった形にするだけです。SMS・通話への送り出しは含まず、それは通知の継手（D）の側です。",
        "正規化できたことは、番号が存在する・つながる・誰かのものである・その人に連絡してよいことの証明ではありません（existence_verified はいつも false）。",
        "文字は ASCII だけを見ます。全角の数字などを半角にする変換・Unicode の正規化はせず、丸かっこの対応・飾りの置き場所は検めません。",
        "表は 3 地域に限った保守的なものです。地域を足すときは表の版を上げ、試験とオラクルを足して再検証する別の作業になります。",
        "正規化できたことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の電話番号ライブラリ（libphonenumber など）の写しや使い方の雛形ではありません。"
      ],
      "integration": "src/phone.js の normalize(text, opts) を呼びます（投げません）。受け取るのは 64 文字までの文字列 text と、省略か { default_region } だけの opts（国内の形では必須）です。返すのは { ok: true, errors: [], e164, region, country_code, national_number, metadata_revision, existence_verified: false } か { ok: false, errors: [{ code }], e164: null, … }（code は error_taxonomy）。toE164 は受け取れなければ PhoneError を投げます。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-07",
      "category": "L/地域化・整形",
      "kind": "Tsugite",
      "title": "暦の参照データ — 地域対応の運び継手（Tsugite）",
      "summary": "地域対応の運び継手（Tsugite）は、暦の参照データの項目では、祝日の暦などの参照データの要求（ReferenceDataRequest）を、pin した基盤・データセット・locale で検めてから参照データの基盤へ運び、戻った receipt（ReferenceDataReceipt）を契約どおりに照合する継手（つぎて）です。参照データを取ってくるだけで、和暦や営業日などの日付の計算はしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "参照データの基盤へ要求を運ぶとき、pin していない基盤・データセット・locale へ送ってしまうこと、そして取ってきた参照データを、正本の暦や法域の権威と取り違えることを防ぎます。",
      "use_when": [
        "祝日の暦などの参照データを、pin した基盤・データセットから取ってきたいとき。",
        "参照データの locale（locale_ref）を、pin した一覧に限りたいとき。",
        "外への効果の無い取得（副作用 NONE）として運びたいとき。",
        "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には data_state・data_ref・authoritative_status・jurisdiction_authority が添えられます。",
        "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "和暦への変換や営業日の計算など、日付の計算をさせたいとき。この継手は参照データを取ってくる要求を運ぶ継手で、日付の計算の機能は含みません。",
        "取ってきた参照データを、正本の暦や法域の権威として扱いたいとき。そう名乗る receipt は受理しません。",
        "時刻の変換など、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
        "参照データの正しさを確かめたいとき。正本の暦の判断は、外の判断と人・組織の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
      ],
      "what_it_checks": [
        "pin していない基盤・locale・データセットへの要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
        "正本の参照データ（authoritative）や法域の権威（jurisdiction authority）を名乗る receipt は受理しません。",
        "できなかった（FAILED・UNAVAILABLE・NOT_FOUND）のに、出力の参照（data_ref）や効果を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "日付の計算（和暦・営業日）はしません。この継手は参照データを取ってくる要求を運ぶ継手で、日付の計算の機能は含みません。",
        "参照データは、正本の暦でも法域の権威でもありません。ERABAZU は法域の権威を名乗りません。正本の暦の判断は、外の判断と人・組織の役目です。",
        "参照データの正しさは確かめません。",
        "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
        "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に ReferenceDataRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は NONE です（参照データ）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "L-08",
      "category": "L/地域化・整形",
      "kind": "Primitive",
      "title": "照合順序（collation） — 照合順序の部品（Primitive）",
      "summary": "照合順序の部品（Primitive）は、文字列を言語の照合順序で比べ、並べる単独の部品です。照合そのものは Node.js の組み込みの Intl.Collator（ICU）に任せ、この部品は locale（ja・en）と強さ（base・accent・variant）を許可リストで固定し、頼んだとおりに解決されなければ止めます。照合で同じ（sameKey）ことは同一ではありません。受け取れない値は決まったコードで止めます（fail-closed）。",
      "problem": "一覧の裏で、並び順が実行環境の既定の locale に黙って落ちて変わってしまうこと、照合で同じ文字列を同一とみなしてしまうこと、照合で同じ文字列どうしの順が定まらないことを防ぎます。結果に ICU の版を添えるので、どの版の順かを後から確かめられます。",
      "use_when": [
        "文字列の配列を ja か en の照合順序で並べ、結果と ICU の版を受け取りたいとき（sortList。例外を投げません）。",
        "照合で同じ文字列どうしを元の位置の順に残したいとき（sortStable）、または UTF-16 の符号単位の順で決めて全順序にしたいとき（sortTotal）。",
        "2 つの文字列を比べて -1・0・1 のどれかを受け取りたいとき（compare。反対称で -0 を返しません）。",
        "実行環境の既定の locale に依らず、明示した locale だけで並べたいとき。Collator の解決が頼んだとおりでなければ止めます。",
        "入力を書き換えずに並べたいとき。すでに並んでいても新しい配列を返し、要素の getter を呼びません。"
      ],
      "do_not_use_when": [
        "ICU の版にかかわらず同じ順が要るとき。順は実行環境の ICU（78.3）に依り、版が変われば同じ入力でも順が変わりえます。ほかの版では確かめていません（icu.matches_pin が false になります）。",
        "重複の判定や鍵に使いたいとき。照合で同じことは同一ではありません（例: ja の variant で「あ」と「ア」）。",
        "漢字の読み（音読み・訓読み）の順で並べたいとき。ja の漢字の順は ICU の ja の規則（JIS の順）です。読みの順で並べるときは、使う側が読みを渡して並べます。",
        "ja と en のほかの locale（ja-JP・EN・und を含む）を使いたいとき。UNSUPPORTED_LOCALE で止めます。",
        "数の照合（\"10\" と \"2\" を数として比べる）が要るとき。この部品はしません。",
        "Node.js v24.21.0（ICU 78.3）のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "opts が素のオブジェクトでない、キーが locale・strength（sortList は order も）ちょうどでない、order が stable・total のどちらでもないとき、INVALID_OPTIONS で止めます。",
        "locale が ja・en のどちらでもないとき UNSUPPORTED_LOCALE、strength が base・accent・variant のどれでもないとき UNSUPPORTED_STRENGTH で止めます。",
        "Intl.Collator が頼んだ locale と固定の設定のとおりに解決されないとき（ICU にその locale が無いなど）、ほかの locale に黙って落ちずに LOCALE_NOT_AVAILABLE で止めます。",
        "比べる値・要素が文字列でないとき（String オブジェクト・穴・getter の要素を含む）NOT_A_STRING、65,536 符号単位を超えるとき STRING_TOO_LONG で止め、要素なら index に位置を返します。",
        "list が配列でないとき（配列のようなオブジェクト・Set を含む）NOT_AN_ARRAY、100,000 要素を超えるとき LIST_TOO_LONG で止めます。",
        "sortList で値を調べること自体が例外になるとき（Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。"
      ],
      "what_it_does_not_do": [
        "照合順序を自分では持ちません。照合順序は ICU（78.3）のもので、独立には作り直していません。ファズのオラクルも同じ ICU の Collator を使うので、ICU の順が正しいかは確かめていません（確かめたのは並べ替えの性質・安定性・同点の決め方・止めるコードと、ja・en の既知の答え数組）。",
        "並び順が使う人の期待（辞書・電話帳・読みの順など）と同じとは限りません。どの順で見せるかの判断は使う側に残ります。",
        "sortTotal の符号単位の比べで、Unicode の正規化はしません。",
        "Proxy の罠を INSPECTION_FAILED に変えるのは sortList だけです。投げる関数（compare・sameKey・sortStable・sortTotal）は罠の例外をそのまま外に出します。",
        "並べられたことは、その順が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の並べ替えのライブラリの使い方の雛形ではありません。"
      ],
      "integration": "src/collation.js の sortList(list, { locale, strength, order }) を呼びます（投げません）。受け取るのは 100,000 要素までの文字列の配列と、3 つのキーだけの素のオブジェクトです。返すのは { ok: true, errors: [], sorted, icu, … } か { ok: false, errors: [{ code, index? }], icu }（code は error_taxonomy）。compare・sameKey・sortStable・sortTotal は受け取れない値で CollationError を投げます。副作用（NONE）・外の接続・資格情報は無く、利用側が用意するものはありません。状態は Collator の覚え置き（プロセスの中だけ）で、答えに影響しません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-01",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "ビデオ通話の呼制御（WebRTC/SFU） — 双方向の通信の運び継手（Tsugite）",
      "summary": "双方向の通信の運び継手（Tsugite）は、ビデオ通話の呼制御（WebRTC/SFU）の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。映像・音声そのもの（WebRTC・SFU のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
      "use_when": [
        "ビデオ通話の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
        "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
        "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
        "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "映像・音声そのもの（WebRTC・SFU のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、映像・音声を運ぶ機能は含みません。",
        "映像・音声の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
        "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
      ],
      "what_it_checks": [
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
        "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
        "合図の op と合わない呼の状態の receipt は受理しません。",
        "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "映像・音声そのもの（WebRTC・SFU のメディア）は運びません。ビデオ通話の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
        "映像・音声の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
        "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
        "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
        "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の通話・チャットサービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-02",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "音声通話の呼制御（SIP） — 双方向の通信の運び継手（Tsugite）",
      "summary": "双方向の通信の運び継手（Tsugite）は、音声通話の呼制御（SIP）の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。音声そのもの（SIP のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
      "use_when": [
        "音声通話の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
        "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
        "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
        "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "音声そのもの（SIP のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、音声を運ぶ機能は含みません。",
        "音声の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
        "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
      ],
      "what_it_checks": [
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
        "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
        "合図の op と合わない呼の状態の receipt は受理しません。",
        "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "音声そのもの（SIP のメディア）は運びません。音声通話の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
        "音声の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
        "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
        "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
        "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の通話・チャットサービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-03",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "プレゼンス（公開を運ぶ） — 双方向の通信の運び継手（Tsugite）",
      "summary": "双方向の通信の運び継手（Tsugite）は、プレゼンスの項目では、プレゼンスの公開の要求（PresencePublishRequest）を、pin した本人（プレゼンスを出してよい本人の参照）で検めてから通信基盤へ運び、戻った receipt（PresencePublishReceipt）を契約どおりに照合する継手（つぎて）です。「オンライン」を出しても、人がいるとは限りません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "プレゼンスを通信基盤へ出すとき、出してよいと決めていない本人のプレゼンスを出してしまうこと、本人の参照にメールアドレスや電話番号を置いてしまうこと、そして「オンライン」を人がいることと取り違えることを防ぎます。",
      "use_when": [
        "在席・状態（プレゼンス）の公開を、pin した本人の参照（仮名）に限って通信基盤へ運びたいとき。",
        "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には publish_state・human_availability が添えられます。",
        "プレゼンスの公開を、取り消せる外への効果（EXTERNAL_REVERSIBLE）として扱いたいとき。",
        "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "「オンライン」を、人がそこにいることの確認として使いたいとき。そう名乗る receipt は受理しません。",
        "出してよいと決めていない本人のプレゼンスを出したいとき。pin していない本人のプレゼンスは出しません。",
        "本人の参照にメールアドレスや電話番号をそのまま使いたいとき。その要求は送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
      ],
      "what_it_checks": [
        "pin していない本人のプレゼンスは出しません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
        "本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
        "人がいること（human availability）を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "「オンライン」を出しても、人がいるとは限りません。人がいることは確かめません。",
        "プレゼンスを出してよい本人を決めません。どの本人を出すかは、利用側が pin する一覧（presence_subjects）で決めます。",
        "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
        "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の通話・チャットサービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に PresencePublishRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-04",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "ライブ字幕伝送（字幕の区切りを digest で運ぶ） — 双方向の通信の運び継手（Tsugite）",
      "summary": "双方向の通信の運び継手（Tsugite）は、ライブ字幕伝送の項目では、字幕の区切りをリアルタイムのメッセージの要求（RealtimeMessageRequest）として、pin したチャンネルで検めてから通信基盤へ運び、戻った receipt（RealtimeMessageReceipt）を契約どおりに照合する継手（つぎて）です。字幕の本文は運ばず、digest だけを運びます。字幕の正しさは確かめません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "字幕の区切りを通信基盤へ運ぶとき、本文を digest の欄に置いて送ってしまうこと、pin していないチャンネルへ送ってしまうこと、そして届いた・読まれたを、本人の確認や理解・権限と取り違えることを防ぎます。",
      "use_when": [
        "ライブ字幕の区切りを、本文ではなく digest（payload_digest）で、pin したチャンネルへ運びたいとき。",
        "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には delivery_state・delivered・identity_verification・comprehension・authority_assertion が添えられます。",
        "受け手の参照にメールアドレス・電話番号の形を置かずに運びたいとき。",
        "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "字幕の本文そのものを運びたいとき。この継手は字幕の区切りを digest で運ぶ継手で、本文は運びません。本文を payload_digest に置いた要求は送りません。",
        "字幕の正しさを確かめたいとき。字幕の正しさは確かめず、外の判断と人・組織の役目です。",
        "字幕の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
        "届いた・読まれたを、本人の確認や理解・権限として扱いたいとき。そう名乗る receipt は受理しません。",
        "届いたメッセージを取り消したいとき。メッセージの副作用は取り消せない外への効果（EXTERNAL_IRREVERSIBLE）です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
      ],
      "what_it_checks": [
        "pin していないチャンネルへの要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
        "本文を payload_digest に置いた要求、形の崩れた digest の要求は送りません。",
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
        "受け手の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
        "本人の確認（identity verification）・理解（comprehension）・権限（authority）を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "字幕の本文は運びません。運ぶのは字幕の区切りの digest だけです。字幕の本体の運搬・品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
        "字幕の正しさ、届いたメッセージの中身は確かめません。",
        "届いた・読まれたは、本人の確認でも理解・同意でも権限でもありません。",
        "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
        "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の通話・チャットサービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に RealtimeMessageRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_IRREVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-05",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "大容量・再開可能転送 — 大容量・再開可能転送継手（Tsugite）",
      "summary": "大容量・再開可能転送継手（Tsugite）は、大きな中身を 1 つの object として保管先へ送るために、転送を始め、区切り（chunk）を committed_offset から順番どおりに送り、途中で止まったら再開位置を問い合わせて続け、全部そろって中身の sha256 が始めに宣言した payload_digest と同じときだけ確定する継手（つぎて）です。区切りの受け取りや確定を、中身の受け入れとは扱いません（fail-closed）。",
      "problem": "巨大なファイルを区切って送るとき、途中の障害で結果が分からない区切りを二重に積んだり、欠けや違う中身のまま「送れた」と扱ったりすることを防ぎます。区切りの長さと sha256 は送る前に自分で確かめ、全体の sha256 は受け取られた区切りだけで積み上げます。",
      "use_when": [
        "1 回で送れない大きさのファイルを、区切って保管先へ送りたいとき。",
        "転送が途中で止まったとき、再開位置（committed_offset）を問い合わせて続きから送りたいとき。",
        "全部そろった中身の sha256 が始めの payload_digest と同じことを確かめてから、確定したいとき。",
        "結果の分からない区切りを、二重に積まずに解きたいとき。再開位置の問い合わせか、同じ要求の再送（lookup でも解けます）で解きます。",
        "状態の食い違う receipt が出たら、人が確かめるまで転送を止めたいとき。"
      ],
      "do_not_use_when": [
        "区切りを並列に送ったり、順不同で受け取ったりしたいとき。契約 v1 の範囲外で、この継手は送りません。",
        "1 回で送れる大きさの保管だけが要るとき。それはオブジェクト保管（C-01）の継手です。",
        "確定を、中身が正しい・受け入れてよいという判断として扱いたいとき。確定は中身の受け入れではありません（content_acceptance は NOT_DERIVED）。",
        "別のプロセスからの再開や、再起動をまたぐ再開をしたいとき。転送の状態はこの継手の中だけにあり、再起動で消えます。",
        "すぐに使える保管先がほしいとき。実の保管の基盤には一度も接続しておらず、実の転送は 0 回です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "区切りの順番を検めます。committed_offset から順番どおりでない区切り（飛ばした offset、戻った offset、全体の大きさを超える区切り）、最後のほか chunk_size でない区切りは送らず、基盤へも送りません。",
        "区切りの中身を検めます。中身の長さまたは sha256 が宣言と違う区切りは送りません。送る前に自分で確かめます。",
        "確定を検めます。全部の区切りがそろう前の確定と、始めの payload_digest と違う sha256 での確定は送りません。COMPLETED は MATCH の証拠と stored_digest がないと受け取りません。",
        "設定の範囲を検めます。許していない content-type、上限を超える全体の大きさ・区切りの大きさ、allow_worm が true でないときの WORM（取り消せない確定）は送りません。",
        "転送の状態を検めます。未解決の区切りがあるうちの別の送信、期限切れ・閉じた・確定した・中止した転送への送信は送りません。中止の後は再開できません。",
        "receipt を照合します。状態の食い違うもの、外への効果を増やすもの、別の要求の request_digest を名乗るもの、受け入れを名乗る完了は受理せず、受理できない receipt で停止します（HALTED）。",
        "基盤の障害を検めます。障害で結果が分からなくなった区切りは UNKNOWN とし、再開位置の問い合わせの答えが合わなければ受理しません。何が残ったか分からない効果は受け取りません。"
      ],
      "what_it_does_not_do": [
        "巨大ファイルそのものの保管はしません。この継手は、保管の基盤へ区切りを運び、返る receipt を照合するだけです。実の保管の基盤には一度も接続しておらず、実の転送は 0 回です。",
        "並列の分割も、順不同の受け取りもしません。契約 v1 の範囲外で、区切りは committed_offset から順番どおりだけです。",
        "中身そのものの検査はしません。確認・受け入れ・保持の判断は、外の判断と人・組織の役目です。確定は中身の受け入れではありません（content_acceptance は NOT_DERIVED）。",
        "転送の状態を永続化しません。状態はこの継手の中だけで、再起動で消えます。別のプロセスからの再開もしません。",
        "期限（expires_at）は形だけを検めます。期限切れは基盤の答えから知ります。",
        "どの結果も承認ではありません（approval: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のクラウド保管サービスの分割アップロードの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/object-store-resumable/v1でつなぎます。createResumableClient({ cfg, port }) の submit(request, bytes) に TransferStart・ChunkSend・ResumeQuery・TransferComplete・TransferAbort を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。bytes は ChunkSend だけ。用意するもの: 基盤への port（submit、任意で lookup）。副作用は WORM の確定と中止が EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "M-06",
      "category": "M/通信路",
      "kind": "Tsugite",
      "title": "画面共有の呼制御 — 双方向の通信の運び継手（Tsugite）",
      "summary": "双方向の通信の運び継手（Tsugite）は、画面共有の呼制御の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。画面そのもの（画面共有のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
      "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
      "use_when": [
        "画面共有の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
        "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
        "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
        "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
      ],
      "do_not_use_when": [
        "画面そのもの（画面共有のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、画面を運ぶ機能は含みません。",
        "画面の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
        "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
      ],
      "what_it_checks": [
        "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
        "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
        "合図の op と合わない呼の状態の receipt は受理しません。",
        "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
        "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
        "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "画面そのもの（画面共有のメディア）は運びません。画面共有の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
        "画面の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
        "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
        "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
        "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の通話・チャットサービスの使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-01",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "サービス発見・登録 — サービス発見・登録の継手（Tsugite）",
      "summary": "サービス発見・登録の継手（Tsugite）は、外の登録の基盤（registry / control plane）へ、サービスの instance の登録（REGISTER、lease つき）・登録の取り消し（DEREGISTER）・lease の延長（HEARTBEAT）・照会（QUERY）の要求（RegistryRequest）を検めてから運び、返った receipt（RegistryReceipt）を契約どおりに照合する継手（つぎて）です。照会の結果は時刻つきの観測で、載っている endpoint を正しい・健全・許可された・本物とは扱いません（fail-closed）。",
      "problem": "相手の所在を外の登録の基盤に頼るとき、期限の過ぎた lease を登録済みと扱うこと、古い instance を健全なまま返すこと、そして「載っている」ことを正しさ・許可・身元と取り違えることを防ぎます。lease の期限と照会の古さは、注入した時計で確かめます。",
      "use_when": [
        "マイクロサービスの instance を、外の登録の基盤に lease つきで登録し、HEARTBEAT で延ばし、不要になったら取り消したいとき。",
        "相手の所在を照会し、古い instance も隠さずに、stale: true と effective_health: STALE の印つきで受け取りたいとき。",
        "使ってよい registry・service・登録してよい endpoint・ttl の上限を設定で決め、その外へは送りたくないとき。",
        "lease の期限や照会の古さを、登録の基盤の言い分ではなく、注入した時計で確かめたいとき。"
      ],
      "do_not_use_when": [
        "手元だけの静的な名前解決がほしいとき。この継手は外の登録の基盤へ運ぶ継手で、手元の名前解決は別の Primitive の範囲です。",
        "照会で得た endpoint を、そのまま本物・健全・呼んでよい相手として使いたいとき。この継手は確かめません（endpoint_identity は NOT_VERIFIED、correctness・authority は NOT_DERIVED）。",
        "lease の状態を再起動の後も持ち越したいとき。状態はこの継手の中だけで、再起動の後は前に登録した instance の HEARTBEAT・DEREGISTER を送りません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
        "すぐに使える登録の基盤への接続がほしいとき。実の登録の基盤には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "設定を検めます。設定が無い、一覧に無い registry・service・endpoint、register_endpoints・max_ttl_seconds の無い REGISTER、stale_after_seconds の無い QUERY は送りません。",
        "ttl を検めます。整数でない ttl、設定の上限を超える ttl、86400 を超える ttl・上限の設定は送りません。",
        "時計を検めます。時計が無い・壊れていれば何も送らず、receipt も受け取りません。",
        "lease の扱いを検めます。まだ有効な lease がある instance への REGISTER、lease の無い・期限の過ぎた lease への HEARTBEAT、この継手が登録していない instance の DEREGISTER は送りません。",
        "参照と要求の形を検めます。.. や絶対パス・ドライブ・制御文字を含む参照、Ref でない request_id、形の悪い service_name、未知の op、op が使わない欄は送りません。",
        "receipt を照合します。request_digest の偽り、request_id・registry・service_name・op の食い違い、表の外の状態、効果を増やす・不明な効果は受理せず、停止します（HALTED）。",
        "lease と照会の中身を照合します。依頼した ttl より長い lease、時刻の形でない lease、未来の last_seen_at、同じ instance が二度載った照会、要求の前や今より後の観測時刻の receipt は受け取りません。",
        "receipt が正しさや許可を導いていたら（correctness・authority が NOT_DERIVED でない）受理しません。"
      ],
      "what_it_does_not_do": [
        "載っている endpoint が本物か（身元・証明書）、正しい・健全・呼んでよいかは確かめません。observed_health は登録の基盤の観測をそのまま運ぶだけです。身元の確かめは、たとえば B-09 の証明書と呼ぶ側の信頼の方針の役目です。",
        "登録・照会できたことは承認ではありません（approval: false）。呼んでよいかの判断は、外の判断と人・組織の役目です。",
        "instance の lease_expires_at の長さが妥当かは確かめません（契約に基準がありません）。遠い先の lease を返す基盤では、古い instance が古いと出ないことがあります。他人の instance の DEREGISTER はしません。",
        "時計そのものの正しさと、登録の基盤の時計との実際のずれは確かめません。",
        "lookup の無い port で、結果不明の送信がもう検めを通らないときは、未解決のまま止まります。効果が UNKNOWN の FAILED も受け取らず停止し、登録が残ったかは人が確かめます。",
        "登録の基盤の認証に使う資格情報は持ちません。資格情報は port 側が持ちます（HELD_BY_PORT）。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の登録の基盤（サービスメッシュ・クラウドの名前解決など）の使い方の雛形ではありません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/service-registry/v1 でつなぎます。createServiceRegistryClient({ cfg, port, clock }) の submit(request) に RegistryRequest を渡すと、DONE（result に op ごとの事実。QUERY では instances・endpoint_identity）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: 登録の基盤への port（submit、任意で lookup）と時計 clock。副作用: QUERY は NONE、ほかの 3 つは EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-02",
      "category": "N/連携基盤",
      "kind": "Component",
      "title": "API ゲートウェイ・ルーティング — API ゲートウェイ・ルーティングの部品（Component）",
      "summary": "API ゲートウェイ・ルーティングの部品（Component）は、ゲートウェイの経路の表を、手元で検めて（部品(Primitive) route-config）から、ゲートウェイへ反映する（継手 gateway-config）ために束ねる部品です。変更は 2 段で、prepareApply・prepareRollback が人・組織の承認する対象（authority subject）とその digest を返し、その digest を含む根拠を添えて apply・rollback を呼びます。根拠の無い変更、根拠の対象と違う変更、検めが断った表は送りません（fail-closed）。反映できたことは、流れが正しいことでも変更の承認でもありません。",
      "problem": "認証や流量の集約点であるゲートウェイの裏で、検めを通っていない表や、検めた digest と違う表を反映してしまうこと、人・組織の根拠の無い変更や別の変更への承認で反映してしまうこと、結果の分からない変更を別の要求で送り直してしまうことを防ぎます。",
      "use_when": [
        "経路の表を手元で検め（形・参照・衝突・隠れた経路）、検めが通った表だけをゲートウェイへ APPLY したいとき。checkRoutes に route-config の check、gateway に gateway-config の継手のクライアントを注入します。",
        "変更を 2 段に分けたいとき。prepareApply・prepareRollback は何も送らずに、承認する対象（gateway_ref・op・base_version_ref・config_digest または rollback_to_version_ref）とその digest を返し、apply・rollback はその digest を含む人・組織の根拠（origin: HUMAN か ORGANIZATION、evidence_ref、subject_digest）を受け取ります。",
        "APPLY に、手元の検めの出力から継手が作った証跡（validation_evidence）を載せ、続けて人・組織の根拠を継手へ渡してから送りたいとき。",
        "前の版へ戻す（rollback）ときも、人・組織の根拠を継手へ渡してから送りたいとき。",
        "今の版を効果なしで観測し（observe、STATUS）、rollout_of_request_id を渡したときは、その反映が終わったかを継手の結果のまま受け取りたいとき。observe は保留中でも送れます。",
        "結果の分からない変更を保留にし、resolvePending で同じ要求でだけ送り直したいとき。base_version_ref が二重の変更を防ぎます。"
      ],
      "do_not_use_when": [
        "反映できたこと（APPLIED）を、流れが正しいことや変更の承認として使いたいとき。どの結果にも approval: false・authority: NOT_DERIVED・traffic_correctness: NOT_DERIVED が付きます。",
        "人・組織の根拠なしに経路を変えたいとき。経路の変更（APPLY・ROLLBACK）には人・組織の根拠が要り、この部品は根拠を作らず、呼び出し側から受け取ります。",
        "根拠の中身（誰が・いつまで）をこの部品に確かめさせたいとき。この部品は、対象の digest が合うことだけを確かめて継手へ渡します。",
        "リクエストの振り分け（入口の交通整理）そのものを行う部品が要るとき。この部品は経路の表を検め、振り分けを変える反映を束ねた継手に送らせるだけです。",
        "ゲートウェイが検めと同じ規則で表を読むこと、同じ digest を計算することを当てにしたいとき。どちらも確かめません（route-config の限界のまま）。",
        "本物のゲートウェイで確かめた部品が要るとき。本物のゲートウェイ・制御面には一度も接続していません。",
        "再起動をまたいで保留の変更を続けたいとき。状態はこのプロセスの中だけで、再起動で保留の要求は消えます。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき INVALID_INPUT、入力や port の結果の getter が例外を投げたとき INSPECTION_FAILED で止めます。",
        "手元の検めが表を断ったとき ROUTES_REFUSED（route_errors を添える）、検めの結果の config_digest の形が違うとき（domain・algorithm・64 桁の 16 進・target）CHECK_RESULT_INVALID、検めの関数が投げたとき PORT_FAILED で、APPLY を送りません。",
        "根拠が無い・欄の過不足・出どころが HUMAN か ORGANIZATION でない・参照が不正・subject_digest が 64 桁の 16 進でないとき NO_AUTHORITY_EVIDENCE、根拠の subject_digest がその場で計算し直した対象の digest と違うとき（別の変更の承認）AUTHORITY_SUBJECT_MISMATCH で、APPLY・ROLLBACK を送りません。",
        "継手が検めの証跡を返さないとき EVIDENCE_REFUSED、根拠の記録に true ちょうどを返さないとき AUTHORITY_REFUSED で、送りません。",
        "継手の結果が分からないとき（UNKNOWN・例外）、変更を GATEWAY_UNKNOWN で保留にし、保留の間は resolvePending と observe のほかを CHANGE_PENDING で止めます。保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。",
        "継手が送らなかったとき GATEWAY_REFUSED（gateway_errors を添える）、receipt を照合できず停止したとき GATEWAY_HALTED で、状態を変えずに伝え、保留も残しません。",
        "applied は継手の結果が applied: true のときだけ、rollout_completed は継手の結果が true ちょうどのときだけ true にします。observe（STATUS）は効果なし（intended_side_effect NONE）で送ります。",
        "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。"
      ],
      "what_it_does_not_do": [
        "API ゲートウェイ・ルーティングの部品は継手ではありません。自分の Request/Receipt は無く、振り分けを変えるという外への効果は、束ねた gateway-config の継手が自分の契約（gateway-config/v1）で起こします。この部品自身は外へ触れません。",
        "束ねた部品の意味（検めが通った ≠ 流れが正しい、反映できた ≠ 承認）は、それぞれの部品の側に残ります。",
        "反映できたことは、流れが正しいことでも変更の承認でもありません（approval: false・authority: NOT_DERIVED・traffic_correctness: NOT_DERIVED、derives_authority: false）。",
        "人・組織の根拠を作りません。経路の変更の根拠は呼び出し側から受け取り、根拠の中身（誰が・いつまで）は確かめません。",
        "ACCEPTED・APPLYING・PARTIAL の後に反映が終わったかは、observe に rollout_of_request_id を渡して継手の結果のまま返すだけで、この部品は判定しません。",
        "本物のゲートウェイ・制御面には一度も接続していません。単体試験とファズは代役で、本物の 2 部品のコードとの結線は、継手の試験用のゲートウェイの代役を相手に確かめました。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のゲートウェイ製品の使い方の雛形ではありません。"
      ],
      "integration": "src/routing.js の createGatewayRouting(options) で作ります。受け取るのは gateway_ref・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。各操作は { ok, errors, approval: false, record_pending, last_change, … } を返します。利用側が用意する port は checkRoutes（route-config の check）と gateway（gateway-config の継手のクライアント。submit と、証跡・根拠を渡す 2 つの関数）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-03",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "Webhook 購読レジストリ — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、Webhook 購読（WebhookSubscription）の登録・更新・取り消しの要求を、pin した Webhook の宛先の一覧で検めてから連携先へ送り、戻った receipt を契約どおりに照合する継手（つぎて）です。宛先・参照・receipt のどれかが外れたら、送らず、受理しません（fail-closed）。",
      "problem": "通知先（Webhook の宛先）を外部に登録するとき、任意の callback URL を差し込まれること、receipt で宛先を差し替えられること、そして「登録できた」を「通知が届いた」と取り違えることを防ぎます。",
      "use_when": [
        "通知先（Webhook の宛先）の登録・管理を、外部の連携先に対して行いたいとき。宛先は、あらかじめ pin した一覧の中からだけ選びます。",
        "購読の登録（SUBSCRIBE）・更新（UPDATE）・取り消し（REVOKE）の結果を、receipt の状態（REGISTERED・UPDATED・REVOKED か FAILED）で照合してから扱いたいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。",
        "照合できない receipt を受け取ったら、以後の送信を止めたいとき（RECEIPT_REJECTED のあとは HALTED）。"
      ],
      "do_not_use_when": [
        "Webhook の配送・署名の検証・受領をしたいとき。この継手が扱うのは購読の登録だけです。",
        "購読の receipt を、通知が届いた証拠として使いたいとき。receipt が言えるのは登録の段階（SUBSCRIPTION_REGISTERED）までです。",
        "任意の callback URL を利用者に指定させたいとき。pin していない宛先には送りません。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "宛先を検めます。pin した Webhook の宛先の一覧に無い宛先（callback URL の差し込み）には送りません。空の一覧は何も許しません。",
        "receipt の宛先を照合します。宛先が差し替えられていたら受理せず、停止します（HALTED）。",
        "配送や受領まで言う購読の receipt は受理しません。状態が操作に対応しない（SUBSCRIBE→REGISTERED・UPDATE→UPDATED・REVOKE→REVOKED のどれでもなく FAILED でもない）receipt も受理しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。購読の効果は EXTERNAL_REVERSIBLE（FAILED は NONE）です。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "Webhook の配送・署名の検証・受領はしません。この継手の範囲外です（購読の登録だけ）。",
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（WebhookSubscription）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には subscription_state・subscription_ref・lifecycle_stage が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_REVERSIBLE（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-04",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "データ取込（ETL）コネクタ — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、外部から自系へのデータ取込（DataImport）の要求を、pin した取込元・スキーマ・対応表で検めてから連携先へ送り、戻った receipt の件数と却下の参照を契約どおりに照合する継手（つぎて）です。一部失敗は成功にも失敗にも潰さず、そのまま返します（fail-closed）。",
      "problem": "外部からデータを取り込むとき、pin していない取込元や対応表から取り込んでしまうこと、そして件数や一部の失敗をごまかして「全部取り込めた」と扱ってしまうことを防ぎます。",
      "use_when": [
        "外部から自系へのデータの取り込み（移行・同期）を、pin した取込元・スキーマ・対応表の範囲に限って行いたいとき。",
        "取込の結果を、件数（received = accepted + rejected）と却下の参照（rejected_record_refs）で照合してから扱いたいとき。",
        "一部だけ失敗した取込を、一部失敗（PARTIAL）のまま受け取りたいとき。",
        "取り消せない取込を、設定で明示して許したとき（allow_irreversible: true）だけ送りたいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "取り込んだデータの意味や対応表が正しいかを確かめたいとき。この継手が見るのは件数と参照だけです（semantic_correctness: NOT_VERIFIED）。",
        "取込の突き合わせの判断を任せたいとき。突き合わせは核（reconcile-facts）の役目です。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "取込元・スキーマ・対応表を検めます。pin した一覧に無いものは送りません。空の一覧は何も許しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "取り消せない取込は、設定が allow_irreversible: true のときだけ送ります。そうでなければ送りません。",
        "件数を照合します。received = accepted + rejected にならない receipt、却下の件数と却下の参照の数が合わない receipt、却下の参照に重複がある receipt は受理しません。",
        "結果の状態を照合します。受け入れと却下の片方しかない PARTIAL、却下があるのに完了とする receipt、受け入れがあるのに FAILED とする receipt は受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。取込の効果は EXTERNAL_*（FAILED は NONE）です。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "取り込んだデータの意味の正しさ・対応表の正しさは確かめません（件数と参照だけ）。",
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（DataImport）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には partial_state・件数・rejected_record_refs・semantic_correctness: NOT_VERIFIED が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_*（FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-05",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "データ書出コネクタ — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、自系から外部へのデータ書出し（DataExport）の要求を、pin した行き先・スキーマ・profile で検めてから送り、戻った receipt を契約どおりに照合する継手（つぎて）です。「作れた」「届けた」「受け取られた」を分けて返し、合わない receipt は受理しません（fail-closed）。",
      "problem": "自系のデータを外部へ書き出すとき、pin していない行き先へ送ってしまうこと、そして「作れた」だけで「相手に届いた」「相手が受け取った」と扱ってしまうことを防ぎます。",
      "use_when": [
        "自系から外部へのデータの書き出しを、pin した行き先・スキーマ・profile の範囲に限って行いたいとき。書き出しの選択も、任意で pin できます（selection_refs）。",
        "書出しの結果を、作れた（GENERATED）・届けた（DELIVERED）・受け取られた（ACCEPTED）に分けて扱いたいとき。DONE には delivery_state・delivered・destination_accepted が添えられます。",
        "取り消せない書出しを、設定で明示して許したとき（allow_irreversible: true）だけ送りたいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "作れた（GENERATED）ことを、相手に届いた証拠として使いたいとき。この継手は 3 つの段階を分けて返します。",
        "書き出すデータの意味の正しさを確かめたいとき。意味の正しさは核と人の役目で、この継手は送る前と受け取った後の検めだけをします。",
        "どの連携を許すかを決めたいとき。その決定は核（policy-facts）の役目です。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "行き先・スキーマ・profile を検めます。pin した一覧に無いものは送りません。空の一覧は何も許しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "取り消せない書出しは、設定が allow_irreversible: true のときだけ送ります。そうでなければ送りません。",
        "段階と効果を照合します。作れた（GENERATED）なのに外への効果を言う receipt、届けた（DELIVERED）なのに効果の無い receipt は受理しません。GENERATED の効果は NONE か LOCAL_REVERSIBLE、DELIVERED・ACCEPTED は EXTERNAL_* です。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（DataExport）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には delivery_state・delivered・destination_accepted が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: 段階で異なります（NONE〜EXTERNAL_*）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-06",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "スキーマレジストリ — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、スキーマ登録（SchemaRegistration）の要求を、pin したスキーマの一覧で検めてから連携先へ送り、戻った receipt を契約どおりに照合する継手（つぎて）です。登録できたことを、Schema Authority や Canonical と取り違えません（fail-closed）。",
      "problem": "契約（型）を共有するためにスキーマを登録するとき、pin していないスキーマを登録してしまうこと、そして「登録できた」ことを「そのスキーマが権威（Schema Authority）や Canonical になった」と扱ってしまうことを防ぎます。",
      "use_when": [
        "契約（型）の共有のために、pin したスキーマだけを連携先に登録したいとき。",
        "登録の結果を、receipt の registration_state・schema_authority・canonical_status で照合してから扱いたいとき。",
        "登録の効果を EXTERNAL_REVERSIBLE、重複（DUPLICATE）と失敗（FAILED）を効果なし（NONE）として照合したいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "登録によって、そのスキーマを権威や Canonical にしたいとき。スキーマの登録は Schema Authority も Canonical も生みません。",
        "どのスキーマを権威とするか（Schema Authority）を決めたいとき。それは核と人の役目で、この継手の契約の権威も、まだ定まっていません（NOT_SPECIFIED）。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "スキーマを検めます。pin したスキーマの一覧に無いものは送りません。空の一覧は何も許しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "権威（Schema Authority）を名乗る登録の receipt、Canonical を名乗る登録の receipt は受理しません。",
        "digest の対象を照合します。契約の読みとして、schema_digest の対象は schema_identity、content_digest の対象は request_id です。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。登録の効果は EXTERNAL_REVERSIBLE（DUPLICATE・FAILED は NONE）です。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "スキーマの登録は、Schema Authority も Canonical も生みません。",
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（SchemaRegistration）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には registration_state・schema_authority・canonical_status が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_REVERSIBLE（DUPLICATE・FAILED は NONE）。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-07",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "イベントスキーマ進化 — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、スキーマ互換（SchemaCompatibility）の確認の要求を、pin したスキーマで検めてから連携先へ送り、戻った receipt の互換の主張と試験の結果（test_result）を契約どおりに照合する継手（つぎて）です。互換 UNKNOWN は互換として扱いません（fail-closed）。",
      "problem": "イベントのスキーマを変えるとき、互換が確かめられていない（UNKNOWN）結果や試験に落ちた結果を「互換あり」と扱ってしまい、後方互換を壊すことを防ぎます。",
      "use_when": [
        "後方互換の管理のために、スキーマ互換の確認の結果を照合してから扱いたいとき。",
        "互換の主張（BACKWARD・FORWARD・FULLY）と試験の PASS がそろったときだけ、互換（compatible: true）として扱いたいとき。",
        "pin したスキーマ（イベントのスキーマを含む）に限って、互換を確かめたいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "互換 UNKNOWN や、試験が未実行（NOT_RUN）の主張を、互換として扱いたいとき。この継手はそれを互換として扱いません。",
        "スキーマの意味の正しさや、どれを権威とするか（Schema Authority）を決めたいとき。それは核と人の役目です。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "スキーマを検めます。pin したスキーマの一覧に無いものは送りません。空の一覧は何も許しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "互換が UNKNOWN の receipt は、試験が PASS でも互換として受理しません。",
        "試験に落ちた（FAIL）のに互換（compatible: true）とする receipt、互換の主張（BACKWARD・FORWARD・FULLY）と FAIL が並ぶ矛盾した receipt は受理しません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。互換の確認の効果は NONE です。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（SchemaCompatibility）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には compatibility・test_result・compatible が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-08",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "外部 OAuth 接続管理 — 連携・ETL 継手（Tsugite）",
      "summary": "連携・ETL 継手（Tsugite）は、外部接続の登録（ExternalConnectionRegister）の要求を、pin した接続・credential の参照・scope の一覧で検めてから送り、戻った receipt を契約どおりに照合する継手（つぎて）です。実の OAuth の手続きや token の発行・保管はせず、接続できたことを業務の許可と取り違えません（fail-closed）。",
      "problem": "連携アカウントの接続を登録するとき、token や鍵の値を参照の代わりに渡してしまうこと、許可していない scope で登録してしまうこと、そして「接続できた」を「業務として許された」と扱ってしまうことを防ぎます。",
      "use_when": [
        "連携アカウントの接続を、pin した接続（connection_refs）・credential の参照（credential_refs）・許可する scope（allowed_scopes）の範囲に限って登録したいとき。",
        "credential を値（bytes）ではなく、参照だけで渡したいとき。",
        "登録の結果を、receipt の connection_state・credential_binding_state・business_authorization で照合してから扱いたいとき。",
        "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "do_not_use_when": [
        "OAuth の手続きを行わせたいとき、token を発行・保管させたいとき。この継手はそれをしません。credential の参照が何を指すかの解決は secret-ref／portable-credential-ref の仕組みの役目です。",
        "接続できたことを、業務の許可として使いたいとき。どの連携を許すかの決定は核（policy-facts）の役目です。",
        "token や鍵の形の値を見分けて守る仕組みとして頼りたいとき。この検出は代表的な形だけを見る網で、すべての秘密を見分けるものではありません。本当の守りは pin した参照の一覧です。",
        "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
        "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "接続・credential の参照・scope を検めます。pin した一覧に無い接続や参照、許可していない scope は送りません。空の一覧は何も許しません。",
        "credential_ref が token や鍵の形なら、pin されていても送りません。",
        "接続できたことから業務の許可を導く receipt は受理しません。",
        "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
        "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。接続の登録の効果は NONE か LOCAL_REVERSIBLE です。",
        "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
      ],
      "what_it_does_not_do": [
        "実の OAuth の手続き、token の発行と保管はしません。credential の参照が何を指すかの解決は secret-ref／portable-credential-ref の仕組みの役目です。",
        "token や鍵の形の検出は代表的な形だけを見る網で、すべての秘密を見分けるものではありません。",
        "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
        "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
        "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
        "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（ExternalConnectionRegister）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には connection_state・credential_binding_state・business_authorization が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: NONE か LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-09",
      "category": "N/連携基盤",
      "kind": "Primitive",
      "title": "取得の記録 — 取得の記録（fetch.json）の部品（Primitive）",
      "summary": "取得の記録（fetch.json）の部品（Primitive）は、いつ・どこから・何を取得したかの事実だけを書いた記録 fetch.json（schema erabazu.raw.fetch/1）を作り、検める単独の部品です。中の fetch_record は契約 raw-fetch/v1 の RawFetchReceipt.fetch_record と同じキー・同じパターンです。記録は RFC 8785 で正準化し、その sha256 を返します。何も取得しません。",
      "problem": "取り込みの来歴の土台になります。受け取れない記録は黙って読み飛ばさずに決まったコードと位置で止め、事前署名 URL の痕跡（://・X-Amz-Signature・X-Amz-Credential・Signature=、クエリつきの URL の形）を記録のどこにも入れません。記録の hash は正準の bytes の sha256 で、キーの順や空白に左右されません。",
      "use_when": [
        "取得の事実（source・platform_profile・retention_class・kind_declared・fetch_state・fetch_record）から、fetch.json の記録を作りたいとき（buildFetchRecord）。schema と folder_stamp は部品が足します。",
        "契約 raw-fetch/v1 の受け取り（RawFetchReceipt）の fetch_record を、そのまま写して記録に入れたいとき。",
        "保管した記録を読み戻して検め、同じ canonical と record_sha256 を得たいとき（checkFetchRecord。folder_stamp は fetched_at から作り直して比べる）。",
        "has_more が true のまま止まった取得（PARTIAL）も、取れた分の記録として残したいとき。",
        "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。fetched_at は呼ぶ側が渡します。"
      ],
      "do_not_use_when": [
        "ファイルがあるか、byte_size と sha256 が中身と合うかを確かめたいとき。それは I-10 の役目で、この部品は検めません。",
        "記録に秘密が入っていないことを確かめたいとき。URL・署名の印の網は粗く、:// を含まない秘密（トークン・鍵の値そのもの）や、印を崩した URL は見つけられません。",
        "取得が本当に FETCHED・PARTIAL だったか、fetched_at が取得した時刻として正しいか・未来でないかを確かめたいとき。写しとして受け取り、形と暦だけを検めます。",
        "保持の方針（retention_class の意味・期間）を決めたいとき。ここでは決めません。",
        "大文字・小文字を区別しないファイルシステムで、name の重なりを見つけたいとき。a と A は別の名前として受け取ります。",
        "宣言された media type が中身に合うか・charset が正しいかを確かめたいとき。宣言のまま記録します（読むのは I-09・I-10）。",
        "契約 raw-fetch/v1 が改訂された後に、写し直さずに使うとき。パターンの文字列は契約の写しで、改訂されたら写し直して検め直す必要があります。Node.js v24.21.0 のほかの版も確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "記録（または build の入力）がオブジェクトでないとき NOT_AN_OBJECT、RFC 8785 で正準化できない値（小数・undefined・getter・Date など）を含むとき NOT_CANONICALIZABLE（detail に I-08 のコード）で止めます。getter は呼ばず、正準化した写しの上で検めます。",
        "決まっていないキーがあるとき（build に schema・folder_stamp を渡した場合を含む）UNKNOWN_FIELD、必須のキーが無いとき MISSING_FIELD で止めます。fetch_params_digest と files[].file_ref も必須です。",
        "schema・source・platform_profile・retention_class・kind_declared・fetch_state が決まった値や形でないとき、INVALID_SCHEMA・INVALID_SOURCE・INVALID_PLATFORM_PROFILE・INVALID_RETENTION_CLASS・INVALID_KIND_DECLARED・INVALID_FETCH_STATE で止めます。fetch_record を持たない状態（NOT_READY・FAILED など）は受け取りません。",
        "fetched_at が契約のパターン（1970 年以降・UTC の Z・うるう秒なし）に合わない、またはその月に無い日（2 月 30 日・平年の 2 月 29 日など）のとき INVALID_FETCHED_AT、folder_stamp が fetched_at から作り直した値と違うとき FOLDER_STAMP_MISMATCH で止めます。",
        "Digest の形（キー・algorithm・value・domain）が違うとき、files[].sha256.target が name と違うとき INVALID_DIGEST、files が空・キーが違う・byte_size が 0 以上の整数でないとき INVALID_FILES、name が契約のパターンでない（.. を含む）とき INVALID_FILE_NAME、同じ name が 2 つ以上のとき DUPLICATE_FILE_NAME、declared_media_type が契約の形でない・255 文字を超えるとき INVALID_MEDIA_TYPE で止めます。",
        "pages が空、seq が 0 から 1 ずつでない、cursor がつながらない、has_more が状態に合わない（FETCHED は最後だけ false、PARTIAL はすべて true）とき、INVALID_PAGES で止めます。",
        "platform_profile が amazon-sp-api-reports・amazon-pay-reports なのに lineage が無いとき LINEAGE_REQUIRED、stripe なのに api_version が null のとき API_VERSION_REQUIRED で止めます（FETCHED でも PARTIAL でも）。lineage の形が違うときは INVALID_LINEAGE です。",
        "記録のどこかの文字列がクエリつきの URL の形を含むとき URL_WITH_QUERY_REFUSED、://・X-Amz-Signature・X-Amz-Credential・Signature= を含むとき URL_FORM_REFUSED で止めます（大文字・小文字を区別せず、探しは線形の時間）。止めたときは、決まった順で最初の 1 つを code と path（JSON Pointer）で返します。"
      ],
      "what_it_does_not_do": [
        "Stripe（Amazon）は接続先として名前を挙げているだけで、提携・承認ではありません。",
        "何も取得しません。ファイルの読み書きもしません。",
        "記録を受け取れたことは、取得が正しかったこと・ファイルがあること・digest が中身と合うこと・秘密が入っていないこと・受け入れてよいことを意味しません。",
        "契約の runtime 規則のうち、要求（request）を持たないと確かめられないもの（fetch_params_digest の target・value が要求と合うか）や、記録の外のことは検めません。",
        "可読性の検め（I-09）、封（H-11）、保持の方針は、この部品の外に残ります。",
        "記録ができたことは、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の取得ツールの記録形式の雛形ではありません。"
      ],
      "integration": "src/fetch-record.js の buildFetchRecord({ source, platform_profile, retention_class, kind_declared, fetch_state, fetch_record }) と checkFetchRecord(record) を呼びます（投げません）。check は schema と folder_stamp を含む 8 つのキーの記録を受け取ります。返すのは { ok: true, errors: [], record, canonical, record_sha256 } か { ok: false, errors: [{ code, path }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みの crypto だけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-10",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "生データの取得（Stripe） — 生データの取得（Stripe）の継手（Tsugite）",
      "summary": "生データの取得（Stripe）の継手（Tsugite）は、Stripe の一覧の API から、頁ごとの応答の本文を受け取った形のまま取り、取得の記録（fetch_record）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。中身は読まず、正規化も判断もしません（fail-closed）。",
      "problem": "連携の取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。受け取ったバイト列の長さと sha256 を計算し直して記録と照らし、カーソルの連鎖・API の版・宣言を確かめ、事前署名の URL を残さず、取得を判断や受け入れと取り違えません。",
      "use_when": [
        "Stripe の一覧の API（charges・payment_intents・refunds・balance_transactions・payouts・disputes・invoices・events）の応答を、受け取った形のまま残したいとき。",
        "受け取ったバイト列の byte_size と sha256 が、取得の記録（fetch_record）と同じことを確かめてから受け取りたいとき。",
        "頁を途中で止めても、続きを取れるようにしたいとき。PARTIAL のときの result.resume_cursor を、次の要求の starting_after にします。",
        "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままです。"
      ],
      "do_not_use_when": [
        "応答の中身を読みたい・型にはめたい・正規化したいとき。共通形への写像・重複の判断・突き合わせは、ほかの層と人・ルールの役目です。",
        "レポートを作らせたいとき（CREATE_REPORT）。この継手が送るのは op FETCH だけで、ほかの profile も送りません。",
        "Stripe の API クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
        "保持の期限を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
        "すぐに使える取得の仕組みがほしいとき。実の Stripe からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "要求を検めます。送るのは profile stripe・op FETCH・limit 1〜100・starting_after と ending_before の一方だけの要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。外れたら送りません（REFUSED）。",
        "資格情報は参照だけです。用途 FETCH・スコープ READ_ONLY と書いた参照だけを使い、署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
        "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。",
        "pages を照合します。seq は 0 から 1 ずつの切れないカーソルの連鎖で、FETCHED は has_more false で終わり、PARTIAL は続きのカーソルを持って止まるものだけを受け取ります。FETCHED と PARTIAL には api_version の文字列が要ります。",
        "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
        "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。",
        "状態の食い違う receipt、効果を増やす receipt（FETCH はどの状態でも NONE）、効果の不明な receipt、別の要求の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。",
        "同じ request_id の別の中身は送りません。Stripe（port）の障害は UNKNOWN とし、読むだけなので同じ要求を送り直して構いません。"
      ],
      "what_it_does_not_do": [
        "Stripe は接続先として名前を挙げているだけで、提携・承認ではありません。",
        "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません。",
        "Stripe の一覧の API の実の振る舞い（並び順・ending_before の向き・has_more）は代役で真似ただけで、実の Stripe とは突き合わせていません。API の版の値の形も確かめません。",
        "時刻（observed_timestamp・fetched_at）は形と実在する日時だけを検め、正しさ・古さは確かめません。1 回で何頁取るか・時間の予算は port の役目です。",
        "照合済みの記録はこの継手の中だけで、再起動で消えます。fetch.json・payload の書き出しは N-09 と保管の役目です。",
        "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile stripe・op FETCH）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Stripe の port（submit が { receipt, files } を返す）と cfg（account_refs・credentials・signing_key_refs）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-11",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "生データの取得（Amazon SP-API のレポート） — 生データの取得（Amazon SP-API のレポート）の継手（Tsugite）",
      "summary": "生データの取得（Amazon SP-API のレポート）の継手（Tsugite）は、Amazon SP-API の Reports から、レポートの文書（TSV の gzip など）を受け取った形のまま取り、取得の記録（fetch_record と lineage）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。中身は読まず、展開も正規化も判断もしません（fail-closed）。",
      "problem": "レポートの取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。文書を gzip のまま運び、長さと sha256・lineage・宣言を照らし、事前署名の URL を残さず、作らせたレポートを二度作らせません。",
      "use_when": [
        "自動で作られるレポート（決済報告書）を、REPORT_PROCESSING_FINISHED の通知（別の port）の後に report_id で取りたいとき（op FETCH）。",
        "オンデマンドのレポートを作らせて report_id を受け取りたいとき（op CREATE_REPORT）。作らせてよい種類は設定 allowed_report_types に書いたものだけです。",
        "受け取った文書の byte_size と sha256 が取得の記録と同じで、GZIP の文書が展開されていないことを確かめてから受け取りたいとき。",
        "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままで、lineage を含みます。"
      ],
      "do_not_use_when": [
        "文書を展開したい・TSV を読みたい・共通形へ写したいとき。それはほかの層の役目で、判断は人・ルールの役目です。",
        "自動で作られる決済報告書（GET_V2_SETTLEMENT_REPORT_…）を作らせたいとき。その種類は設定に入れられません。",
        "REPORT_PROCESSING_FINISHED の通知を受けたいとき。通知の受け口は別の port です。",
        "SP-API の Reports クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
        "保持の期限（顧客の PII の期限など）を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
        "すぐに使える取得の仕組みがほしいとき。実の SP-API からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "要求を検めます。送るのは profile amazon-sp-api-reports の要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。許していないレポートの種類、同じ request_id の別の中身は送りません（REFUSED）。",
        "資格情報は参照だけです。用途 FETCH・スコープ REPORTS_ONLY と書いた参照だけを使い、署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
        "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。GZIP の文書は application/gzip と宣言し、gzip の頭（1f 8b）で始まるときだけ受け取ります。",
        "lineage を照合します。FETCHED と PARTIAL には lineage が要り、FETCH の lineage.report_id は要求の report_id と同じでなければ受け取りません。lineage に文書の URL は書かせません。",
        "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
        "効果を照合します。REPORT_REQUESTED は EXTERNAL_REVERSIBLE をちょうど名乗るものだけを受け取り、ほかの状態は NONE だけです。レポートが作られたか分からない receipt は受け取らず、停止します。",
        "結果の分からない CREATE_REPORT は未解決として覚え、同じ要求の再送で解きます（port.lookup があれば先に探します）。レポートを二度作らせず、解けるまでほかの要求は送りません。",
        "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。照合できない receipt のあとは停止します（RECEIPT_REJECTED のあとは HALTED）。"
      ],
      "what_it_does_not_do": [
        "Amazon は接続先として名前を挙げているだけで、提携・承認ではありません。",
        "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません（GZIP のときの gzip の頭 2 バイトだけは見ます）。",
        "SP-API の実の振る舞い（レポートの処理時間・CANCELLED や FATAL の扱い・文書の URL の寿命・GZIP の有無）は代役で真似ただけで、実の SP-API とは突き合わせていません。",
        "未解決の CREATE_REPORT はこの継手の中だけで覚え、再起動で消えます。lookup の無い port で送り直すと、レポートがもう 1 つ作られることがあります。",
        "どの種類をオンデマンドで作れるかは、設定 allowed_report_types に任せます。決済報告書のほかに自動で作られる種類は知りません。",
        "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile amazon-sp-api-reports）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: SP-API の port（submit と任意の lookup。{ receipt, files } を返す）と cfg の一覧。副作用: CREATE_REPORT だけ EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "N-12",
      "category": "N/連携基盤",
      "kind": "Tsugite",
      "title": "生データの取得（Amazon Pay のレポート） — 生データの取得（Amazon Pay のレポート）の継手（Tsugite）",
      "summary": "生データの取得（Amazon Pay のレポート）の継手（Tsugite）は、Amazon Pay の Reports API から、決済ごとに自動で作られる決済報告書を、通知の後に report_id で受け取った形のまま取り、取得の記録（fetch_record と lineage）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。レポートは作らせません（fail-closed）。",
      "problem": "決済報告の取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。決済報告書を受け取った形のまま運び、長さと sha256・lineage・宣言を照らし、短命のダウンロードの URL を残さず、取得を判断や受け入れと取り違えません。",
      "use_when": [
        "決済報告書の通知（別の port）を受けた後に、その report_id で決済報告書を取りたいとき（op FETCH）。処理中は NOT_READY、無いレポートは REJECTED が返ります。",
        "受け取った決済報告書の byte_size と sha256 が取得の記録と同じことを確かめてから受け取りたいとき。",
        "ダウンロードの URL（30 秒で切れる）を記録に残さずに取りたいとき。URL は port がすぐに使って捨て、継手には渡しません。",
        "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままで、lineage を含みます。"
      ],
      "do_not_use_when": [
        "レポートを作らせたい・予約したいとき。決済報告書は自動で作られ、この継手は CREATE_REPORT を送りません（契約で無効）。",
        "CSV を読みたい・共通形へ写したいとき。それはほかの層の役目で、判断は人・ルールの役目です。",
        "決済報告書の通知を受けたいとき。通知の受け口は別の port です。",
        "Amazon Pay の Reports クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
        "保持の期限を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
        "すぐに使える取得の仕組みがほしいとき。実の Amazon Pay からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。"
      ],
      "what_it_checks": [
        "要求を検めます。送るのは profile amazon-pay-reports・op FETCH・report_id だけの要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。CREATE_REPORT、ほかの profile、同じ request_id の別の中身は送りません（REFUSED）。",
        "資格情報は参照だけです。用途 FETCH と、スコープ READ_ONLY か NO_READ_ONLY_SCOPE_OFFERED（相手が読み取りだけのスコープを持たないと明示した）を書いた参照だけを使います。記録の署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
        "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。GZIP と書かれたときは、application/gzip と宣言し gzip の頭（1f 8b）で始まるときだけ受け取ります。",
        "lineage を照合します。FETCHED と PARTIAL には lineage が要り、FETCH の lineage.report_id は要求の report_id と同じでなければ受け取りません。",
        "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
        "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。",
        "状態の食い違う receipt、効果を増やす receipt（FETCH はどの状態でも NONE）、効果の不明な receipt、別の要求の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。",
        "Amazon Pay（port）の障害は UNKNOWN とします。読むだけなので、同じ要求を送り直して構いません。"
      ],
      "what_it_does_not_do": [
        "Amazon は接続先として名前を挙げているだけで、提携・承認ではありません。",
        "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません（GZIP と書かれたときの gzip の頭 2 バイトだけは見ます）。",
        "Reports API の実の振る舞い（レポートの処理・ダウンロードの URL の寿命・compressionAlgorithm の値）は代役で真似ただけで、実の Amazon Pay とは突き合わせていません。",
        "Amazon Pay の資格情報が読み取りだけのスコープを持てるかは確かめていません。実の鍵の用途は port が確かめます。",
        "照合済みの記録はこの継手の中だけで、再起動で消えます。fetch.json・payload の書き出しは N-09 と保管の役目です。時刻は形と実在する日時だけを検め、正しさ・古さは確かめません。",
        "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile amazon-pay-reports・op FETCH）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Amazon Pay の port（submit が { receipt, files } を返す）と cfg（account_refs・credentials・signing_key_refs）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "O-01",
      "category": "O/検索・索引・取り出し",
      "kind": "Tsugite",
      "title": "索引・検索・取り出し — 索引・検索・取り出しの継手（Tsugite）",
      "summary": "索引・検索・取り出しの継手（Tsugite）は、差し替えられる索引の基盤へ、索引の書込（UPSERT・墓標の UPSERT・DELETE）、検索（KEYWORD・VECTOR・HYBRID・STRUCTURED）、取り出し（source の digest と出所）の要求を、契約と設定で検めてから port へ運び、返った receipt を照合する継手（つぎて）です。見つかったものを事実とはしません（fail-closed）。",
      "problem": "「検索で出た」の裏で、索引の版や鮮度が見えなくなることを防ぎます。設定で狭めて運び、問い合わせの文書の digest と cursor の決定性を照らし、引いた結果を確かめた事実や権威と取り違えません。",
      "use_when": [
        "索引の基盤（既定は利用者の SQLite・FTS5 の部品。差し替えられる）に、索引の書込・検索・取り出しを、同じ検めをかけて運びたいとき。",
        "完全一致の絞り込み（STRUCTURED）で引きたいとき。問い合わせの文書 canonical({filters:[{field, op, value}]}) の sha256 を query_digest と照らしてから渡します。",
        "ページを続けて引くとき、同じ索引・版・問い合わせの ANSWERED の答えで受け取った cursor だけを使いたいとき。",
        "個人情報を消すとき、索引の項目を墓標（tombstone true、content_digest なしの UPSERT）で上書きしたいとき。"
      ],
      "do_not_use_when": [
        "引いた結果を、確かめた事実や権威として使いたいとき。取り出した ≠ 真、似ている ≠ 確かめた事実、順位 ≠ 権威です。結果の確かめ・判断は外の判断と人・組織の役目です。",
        "取り出した中身のバイト列を受け取りたいとき。この継手を通るのは digest と出所だけで、中身が digest と合うかは受け取る側で確かめます。",
        "特定の検索エンジン・ベクトル DB・SQLite の拡張の使い方の雛形がほしいとき。この継手はその雛形ではありません。",
        "Canonical に定まった分類・契約の上で使いたいとき。分類 O と契約 search-retrieval/v1 は DESIGN_CANDIDATE で、この継手もその上の候補です。",
        "すぐに使える索引の基盤がほしいとき。実の基盤への呼び出しは 0 回で、port の実装・設定の値は、接続するときに別の関門で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定は狭めるだけで広げません。index_profiles・index_ops・retrieval_modes・max_page_size・structured_fields・source_prefixes の外の要求は送りません（DELETE を外せば DELETE は送りません）。設定に知らないキーがあれば何も送りません。",
        "STRUCTURED の文書を検めます。形（filters は 1 つ以上、op は EQ・GTE・LT、value は文字列、field は設定の structured_fields）と sha256 が query_digest と合わないときは送りません。結果は score 1 だけ受け取ります。",
        "cursor を検めます。別の index_revision・別の問い合わせの cursor、受け取っていない cursor は送りません。決定的でない cursor、最初の頁と続きの頁で同じ cursor、page_size を超える結果、同じ source の重複は受理しません。",
        "検索の答えを照合します。ANSWERED でない答えは結果も cursor も持たず、鮮度は UNKNOWN のときだけ受け取ります。結果の項目は {source_ref, score, provenance_ref} だけで、verified などの欄は受け取りません。",
        "効果を照合します。INDEXED・DELETED は EXTERNAL_REVERSIBLE、検索・取り出しは NONE、UNKNOWN は索引の書込の FAILED だけです。効果を増やす receipt、別の版や op、別の要求の receipt は受理せず、停止します（HALTED）。",
        "結果の見えない索引の書込（port の障害、FAILED・効果 UNKNOWN の receipt）は未解決として保ちます。送り直す前に、索引の項目を EntryReadRequest / EntryReadReceipt（port.readEntry）で読み、効いていれば送り直しません。読めないときは送り直さず、解けるまでほかは送りません。"
      ],
      "what_it_does_not_do": [
        "ACL を結果を返す前に当てたか、鮮度（freshness_state）が本当か、結果が本当に一致・関連しているかは確かめません（基盤の申告。結果は NOT_VERIFIED のまま運びます）。",
        "STRUCTURED で基盤が値で比べたか、絞り込みの値が宣言した型に合うかは確かめません（基盤が FILTER_INVALID で答えます）。",
        "VECTOR・HYBRID の意味（どの埋め込みか・どう混ぜるか）は契約に無く、score を確からしさとして運ぶだけです。ベクトル検索は基盤の必須にはしません。",
        "cursor の照合と記録はこのクライアントの中だけで、再起動で消えます。再起動の前の cursor ではページを続けられず、最初のページから引き直します。",
        "readEntry の答えが本当に索引の今の項目かは確かめません。readEntry の無い port では、FAILED・UNKNOWN の後は送り直さず、索引に何が残ったかは人が確かめます。",
        "doc_ref・acl_ref・provenance_ref の文字列に個人情報が書かれても見分けません。個人情報そのものを索引に入れないことは運用の決まりです。",
        "運べた・索引に入れたことは承認ではなく、中身を確かめたことでもありません（どの結果にも approval: false が付きます）。契約を変えず、契約の権威は NOT_SPECIFIED です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/search-retrieval/v1（Index・Query・Retrieve・EntryRead の Request と Receipt）でつなぎます。createSearchRetrievalClient({ cfg, port, clock? }) の submit(request, query) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: 索引の基盤の port（submit と任意の lookup・readEntry）と cfg の一覧。副作用: 索引の書込は EXTERNAL_REVERSIBLE、検索・取り出しは NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "O-02",
      "category": "O/検索・索引・取り出し",
      "kind": "Component",
      "title": "取得・取引の索引 — 取得・取引の索引（fetch.db・txn.db）の部品（Component）",
      "summary": "取得・取引の索引の部品（Component）は、索引・検索の継手（O-01）・記録の検め（部品(Primitive) N-09）・取引の事象 ID（部品(Kernel) G-12）を束ね、2 つの索引を作る部品です。fetch の索引にはすべての raw の取得を、txn の索引には取引年月日・取引金額・取引先と raw の参照を、O-01 の UPSERT で入れます。fetch.json は N-09 で、txn_id は G-12 で検め、食い違えば入れません。問い合わせは STRUCTURED で、結果は txn_id の候補です。DELETE は送りません。索引は探す助けで、結果は事実ではありません。",
      "problem": "帳簿の検索の裏にある索引のための部品です。検めていない記録や txn_id の合わない取引を索引に入れること、pii の取引先をそのまま追記だけの索引に入れること、索引の項目を DELETE で消してしまうこと、索引が答えなかった問い合わせを「結果の無い成功」と読むことを防ぎます。",
      "use_when": [
        "fetch.json の記録を N-09 で検め、取得（source・folder_stamp・fetched_at・retention_class・検査の状態と版・ファイルの数）を fetch の索引へ UPSERT で 1 つ入れたいとき（indexFetch）。doc_ref は raw/<source>/<folder_stamp> です。",
        "共通形の取引（erabazu.common.txn/1）の txn_id を G-12 で計算し直し、合った取引だけを txn の索引へ入れたいとき（indexTxn）。文書は occurred_on・amount_key・currency・counterparty_key・raw_refs で、doc_ref は txn_id です。",
        "取引を、取引年月日（EQ・GTE・LT）・金額（amount_key の EQ・GTE・LT。同じ通貨の EQ と組む）・通貨と取引先（EQ だけ）で探し、txn_id の候補を受け取りたいとき（queryTxn）。",
        "金額を、文字列の順が数の順と同じになる 58 文字の固定幅の amount_key に、取引先を counterparty_key（\"id:\" か \"name:\" で始まる値。pii は sha256）にして、問い合わせの値を作りたいとき（amountKey・counterpartyKey）。",
        "UPSERT だけの索引から pii の取引を取り除きたいとき。txn の索引の項目を墓標（tombstone true・content_digest なしの UPSERT）で上書きします（tombstoneTxn）。墓標にした取引は、このインスタンスでは入れ直しません。",
        "結果の分からなかった索引の書込（墓標を含む）を、同じ要求で送り直したいとき（resolvePending）。"
      ],
      "do_not_use_when": [
        "問い合わせの結果を、条件に合う取引の事実や、漏れの無い一覧として使いたいとき。結果は候補（NOT_VERIFIED）で、当たった取引が本当に条件に合うか、索引に漏れが無いかは基盤の申告です。",
        "pii の取引先の sha256 を匿名化として扱いたいとき。sha256 は塩（秘密の鍵）を入れていないので、候補の名前や id を順に試せば当てられます（仮名化で、匿名化ではありません）。日付・金額・通貨・raw の参照は pii の取引でもそのまま索引に入ります。",
        "通貨をまたいで金額を比べたいとき。amount_key の絞り込みは同じ通貨の中だけで意味があり、為替の換算はしません。",
        "全文検索をしたいとき。全文検索（FTS5）は基盤の側で、この部品は構造化した絞り込みだけを送ります。fetch の索引への問い合わせの操作は v1 にありません。",
        "墓標で、保管にある raw まで消したいとき。墓標は索引の項目だけを上書きします。保管が raw を本当に消したかは確かめません。fetch の索引の項目には墓標の操作がありません。",
        "再起動の後やほかのプロセスでも、墓標にした取引の入れ直しを止めたいとき。墓標にした txn_id の控えはこのインスタンスの中だけです。控えを保つ場所は呼び出し側の運用です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が足りない・形が違う・2 つの profile が同じとき INVALID_OPTIONS、入力の形が違うとき INVALID_INPUT、取引の記録が索引に使える形でないとき INVALID_TXN、問い合わせの形が違うとき（amount_key に currency の EQ が無い など）INVALID_QUERY で止めます（where にその位置）。",
        "N-09 が fetch.json を受け取らないとき FETCH_RECORD_REFUSED、N-09 が投げた・答えが食い違うとき CHECK_PORT_FAILED で何も送りません。同じ folder_ref に別の fetch.json、同じ probe_version に別の文書のとき FETCH_INDEX_CONFLICT で送りません（直しは新しい probe_version）。",
        "G-12 が入力を受け取らないとき TXN_ID_REFUSED、G-12 が投げた・答えが食い違うとき TXN_ID_PORT_FAILED、計算し直した txn_id が記録と違うとき TXN_ID_MISMATCH（recomputed_txn_id を添える）で送りません。G-12 には 5 つの事実の写しだけを渡します。",
        "pii として送った txn_id を、ほかの区画で送る・墓標にするとき TXN_CLASS_CONFLICT、墓標にした txn_id を入れ直そうとするとき TXN_TOMBSTONED で送りません。",
        "索引の書込の結果が分からないとき INDEX_UNKNOWN で保留にし、保留の間は INDEX_PENDING で操作を受けません。継手が送らなかったとき INDEX_REFUSED、索引に入らなかった・答えが照合できない・別の doc_ref・別の op・別の index_revision・DELETED のとき INDEX_FAILED です。",
        "問い合わせの結果が分からないとき QUERY_UNKNOWN、送らなかったとき QUERY_REFUSED、結果の検め（score 1・NOT_VERIFIED・送った版・txn_id の形と重なり）に合わない・墓標にした txn_id が答えにあるとき QUERY_FAILED です。索引が答えなかったとき（query_state が ANSWERED でない）は QUERY_NOT_ANSWERED と query_state を返します。",
        "amountKey で金額が形・範囲（38 桁・scale 0〜18）の外のとき INVALID_AMOUNT、counterpartyKey で取引先の形が違うとき INVALID_COUNTERPARTY、区画が 3 つのどれでもないとき INVALID_RETENTION_CLASS です。timestamp が実在する日時を返さないとき CLOCK_FAILED で何も送らず、前の操作が終わっていないとき BUSY です。"
      ],
      "what_it_does_not_do": [
        "取得・取引の索引の部品は継手ではありません。自分の Request/Receipt は無く、索引の書込と問い合わせは、束ねた O-01 の継手が自分の契約（search-retrieval/v1）で行います（問い合わせは NONE）。この部品自身は外へ触れません。",
        "束ねた部品の意味（索引に入れた ≠ 中身を確かめた・似ている ≠ 事実・記録を受け取れた ≠ 取得の正しさ・ID が合う ≠ 取引が実在する）は、それぞれの部品の側に残ります。索引の基盤（SQLite・FTS5）と全文検索は基盤の側です。",
        "結果は事実でも承認でもありません（approval: false・authority: NOT_DERIVED、result_verification: NOT_VERIFIED、derives_authority: false）。",
        "DELETE は送りません。送るのは UPSERT の IndexRequest と STRUCTURED の QueryRequest だけです。",
        "取引の記録は、索引に使う欄とキーの集合だけを検めます。明細などの中身の検めは G-13 の役目です。retention_class は呼び出し側が渡す値をそのまま使い、raw の区画と合っているかは確かめません。文書の中身を基盤へ渡すのは、呼び出し側と基盤の結線の役目です。",
        "本物の O-01・N-09・G-12 には、この部品の試験では接続していません。単体試験とファズは代役です。結線の検めは通りましたが、墓標・query_state などの結線はその検めにありません。実の索引の基盤には接続していません。状態はプロセスの中だけで、再起動で消えます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の検索エンジン・データベースの雛形ではありません。"
      ],
      "integration": "src/ledger-index.js の createLedgerIndex(options) で作り、{ ok, errors, instance } を返します。options は 2 つの索引の profile と revision・request_prefix・ports・任意の timestamp です。操作は indexFetch・indexTxn・tombstoneTxn・queryTxn・resolvePending・status・amountKey・counterpartyKey で、結果に approval: false が付きます。port は index.submit（O-01 の継手のクライアント）・checkFetchRecord（N-09）・computeTxnId（G-12）です。txn の索引の項目の型は TXN_FIELD_TYPES で宣言します。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-01",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "契約・入力の検査 — 契約・入力検査の部品（Kernel）",
      "summary": "契約・入力検査の部品（Kernel）は、スキーマの id と digest・スキーマ・値（JSON の文字列か読み取り済みの値）・能力の宣言から、値がスキーマに合うか（conforms）を事実として出す単独の部品です。スキーマは決まった一部のキーワードだけを扱い、ほかのキーワードを含むスキーマや digest の合わないスキーマは信用せず、適合扱いしません。issue は位置（JSON Pointer）つきで並べます。inference と advice はいつも null です。",
      "problem": "入力の形の検めは、画面の裏で必ず要ります。どのスキーマで検めたか分からないまま適合扱いすること、JSON の重複キーを見落とすこと、pattern の正規表現の破滅的なバックトラック（ReDoS）で処理が止まらなくなることを防ぎます。",
      "use_when": [
        "値が、呼び出し側の渡すスキーマ（type・properties・required・additionalProperties: false・enum・const・items・minItems/maxItems・minLength/maxLength・minimum/maximum・pattern）に合うかを、事実として出したいとき（checkContract）。",
        "スキーマの同一性を確かめてから検めたいとき。schema_ref.digest が \"sha256:\" と RFC 8785 の正準形の SHA-256 の 16 進と一致しなければ、値を検めず SCHEMA_UNKNOWN とし、conforms は false です。digest だけは schemaDigest({ schema }) で出せます。",
        "JSON の文字列（value_text、UTF-8 で 1 MiB まで）を自前の厳密な読み取りで読み、重複キーを DUPLICATE_KEY として位置つきで受け取りたいとき。重複は JSON.parse と同じく後の値で検めます。",
        "足りない欄（missing）と、必要な能力のうち宣言に無いもの（capabilities_missing。capabilities_required から capabilities_declared を引いた集合）も、同じ呼び出しで受け取りたいとき。",
        "未入力を空として扱わず、need_input にしたいとき。digest が無いとき、capabilities_required があって capabilities_declared が無いときは、state が need_input です。",
        "issue を決まった順で 1000 件まで受け取り、切ったことを issues_total と issues_truncated で知りたいとき。",
        "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。キーの並びに関係なく、同じスキーマからは同じ digest ができます。"
      ],
      "do_not_use_when": [
        "完全な JSON Schema の検証が要るとき。参照のキーワード ref・allOf/anyOf/oneOf・format・if/then・null の型・型の配列・タプルの items・数の小数の範囲などは扱わず、UNSUPPORTED_KEYWORD か INVALID_SCHEMA になります。",
        "能力の宣言が本物か（権限の真正性）を確かめさせたいとき。この部品は宣言の集合の差を出すだけで、真正性は Host が確かめます。",
        "pattern に後方参照・後読み・先読み・入れ子の量指定子・\\p・大文字と小文字を区別しない照合・m フラグ・s フラグを使いたいとき。扱わない形は SCHEMA_PATTERN_UNSAFE になり、適合しません。",
        "値が業務として正しいか（スキーマの意味）を判断させたいとき。",
        "読み取り済みの値（value）で重複キーを確かめたいとき。value では重複キーが見えず、duplicate_keys_checked は false です。整数でない数は受け取りますが、どの型・どの enum とも一致しません。",
        "I-01 の入力スキーマ検証の部品と同じ結果を期待するとき。キーワードの範囲も結果の形も違い、互換ではありません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数の形が違うとき（知らないキー・getter・schema_ref か schema が無い・value_text と value の両方がある／どちらも無い・schema_ref の形の誤り）INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
        "知っているキーワードの形が違うとき（type が 6 つの名前でない・required の重複・空の enum・items がスキーマでない・負の件数・pattern が u フラグの正規表現として読めないなど）INVALID_SCHEMA で止めます。知らないキーワードと additionalProperties の false 以外の値は UNSUPPORTED_KEYWORD の issue にし、スキーマを信用しません。",
        "pattern は 128 文字・命令 4,096 個・{n,m} の 1000 までで、後方参照・後読み・入れ子の量指定子は SCHEMA_PATTERN_UNSAFE にします。照合は自前の線形時間の照合器（Pike VM）で、手間の合計を先に数え、50,000,000 を超えるときは照合を始める前に RESOURCE_LIMIT で止めます。",
        "digest が無いときは need_input、違うときは done で、どちらも SCHEMA_UNKNOWN の issue とし、適合しません。書き方は \"sha256:\" と小文字の 16 進だけで、大文字や接頭辞なしは一致しません。capabilities_declared が無いときも need_input です。",
        "value_text が JSON（RFC 8259）として読めないとき（BOM・先頭の 0・制御文字・誤ったエスケープを含む）INVALID_JSON、単独のサロゲートは LONE_SURROGATE で、文字の位置 offset つきで止めます。入れ子 64 段・1 MiB・値の個数の上限を超えるときは RESOURCE_LIMIT です。",
        "value が素の JSON でないとき（undefined・関数・BigInt・NaN・Infinity など）INVALID_VALUE、能力の一覧が 1〜128 文字の重複のない文字列の配列（256 個まで）でないとき INVALID_CAPABILITIES で止めます。\"__proto__\" も普通のキーとして扱い、原型を書き換えません。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。pattern の照合は、オラクル側の Node.js の RegExp（u フラグ）と突き合わせています。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "結果は、スキーマの決まった一部に値が合うかの事実です。値の意味の正しさ・利用の許可・権限の真正性を示すものではなく、データの検証サービスや権限の判定でもありません。",
        "pattern の照合の意味は、Node.js v24.21.0 の RegExp（u フラグ）と、生成した pattern と文字列で突き合わせたものです。ECMAScript の正規表現の全体を証明したものではありません。",
        "照合の手間の上限（pattern_steps）と value の値の個数の上限（value_nodes）は、単体試験だけで確かめました（ファズでは生成していません）。",
        "業務ごとの規則は持ちません。スキーマは呼び出し側が渡します。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/contract-check.js の checkContract({ schema_ref, schema, value_text | value, capabilities_declared?, capabilities_required? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（state・conforms・schema_trusted・issues・missing など、inference: null・advice: null）か { ok: false, errors: [{ code, path, ... }] } です。schemaDigest({ schema }) は digest だけを出します。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-02",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "対応付け・変換・照合 — 対応付け・変換・照合の部品（Kernel）",
      "summary": "対応付け・変換・照合の部品（Kernel）は、元のスキーマのレコードを対応表（元の位置 → 先の位置と変換）で先の形に写し、件数・キー・合計を照合する単独の部品です。当てるのは確認済み（confirmed）で AI 由来（ai_candidate）でない対応だけで、候補は候補のまま並べます。競合する対応は当てず、欠損・未知・0・空を分けて数えます。変換の結果は候補で、確定ではありません。inference と advice はいつも null です。",
      "problem": "移行や連携のデータの写し替えで、確認していない対応や AI の案を事実として当てること、表に無い値を推測で埋めること、意味の違う 2 つの値（入金済と予約枠確保など）を 1 つにまとめてしまうこと、浮動小数点や丸めで合計が狂うことを防ぎます。",
      "use_when": [
        "確認済みの対応だけでレコードを先の形に写したいとき（reconcileMap）。status が candidate の対応と origin が ai_candidate の対応は当てず、candidates_not_applied に理由（STATUS_CANDIDATE・ORIGIN_AI_CANDIDATE）つきで並べます。",
        "変換を 4 つ（copy の写し・enum_map の表による置き換え・unit の 10 進の正確な掛け算・decimal_scale の小数の桁そろえ）に限って使いたいとき。丸めはせず、rounding は \"none\" だけです。",
        "件数・キー・合計を照合したいとき。合計は sum_paths の位置の 10 進の文字列と整数を BigInt で正確に足し、元・写した先・expected を比べた結果と、足せなかった値の数を一緒に出します。",
        "欠損（missing_source_values）・未知（unknown_values。{ \"unknown\": true } のまま写す）・0・空を分けて数えたいとき。0・空の文字列・空の配列は値として写します。",
        "未対応の欄・表に無い値・変換できない値・キーの重複と欠けを、隠さず一覧で受け取りたいとき（unmapped_fields・unmapped_values・transform_failures・duplicate_keys・missing_keys）。",
        "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。外の境界・状態はありません。"
      ],
      "do_not_use_when": [
        "対応の意味が正しいか（同名異義を本当に見分けたか）を判断させたいとき。SAME_NAME_DIFFERENT_TARGET は同じ元の位置から違う先の位置への対応を並べるだけで、違う名前の同じ意味は見つけません。",
        "確認（status: confirmed・origin）が本物かを確かめさせたいとき。呼び出し側の記録によります。AI の案を人が採用したときは、呼び出し側が origin を human か registered にして渡します。",
        "配列の中を添字でたどる位置や、配列の要素ごとの変換が要るとき。v1 は配列をまるごと写すか、未対応の葉として数えます。",
        "単位の体系（次元・換算の正しさ）を確かめたいとき。from と to の名前の文字列を比べるだけで、factor の正しさは呼び出し側が持ちます。",
        "丸めが要るとき。decimal_scale で 0 でない桁を捨てる必要があれば、INEXACT_SCALE として transform_failures に数えます。",
        "変換の結果を適用・保存したいとき。この部品は写した候補を出すだけで、原子的な適用はこの部品の外の役目です。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数の形が違うとき（知らないキー・必要なキーが無い・id と版が 1〜256 文字でない・sum_paths の重複）INVALID_ARGUMENT、値を調べること自体が例外になるとき INSPECTION_FAILED、JSON Pointer の形でない（16 段・1024 文字を超える・単独のサロゲート）とき INVALID_PATH で止めます。",
        "対応表の形が違うとき INVALID_MAP、transform の kind が 4 つ以外か rounding が \"none\" 以外のとき UNSUPPORTED_TRANSFORM で止めます（丸めを暗黙にしません）。",
        "レコードが素の JSON のオブジェクトでないとき（null・整数でない数・単独のサロゲート・{ \"unknown\": true } に似た形）INVALID_RECORDS、expected の形が違うとき INVALID_EXPECTED、上限を超えたとき RESOURCE_LIMIT（limit つき）で止めます。\"__proto__\" のキーで原型を書き換えません。",
        "当てる候補の対応の中で、同じ先に 2 つ（TWO_SOURCES_ONE_TARGET）・先の位置の入れ子（TARGET_PATH_OVERLAP）・同じ元から違う先（SAME_NAME_DIFFERENT_TARGET）・同じ元の unit の from の違い（UNIT_MISMATCH）を競合として並べ、当てません。",
        "1 つの enum_map の表で 2 つ以上の元の値が同じ先の値になるときは MERGED_ENUM_VALUES として並べ、merge_confirmed: true が無ければ当てません。",
        "enum の表に無い値は推測で埋めず unmapped_values に、10 進でない値（NOT_A_DECIMAL）と桁を捨てる値（INEXACT_SCALE）は transform_failures に数え、どちらも converted のその欄を作りません。key_path を読む対応が当たっていなければ state は need_input（KEY_NOT_MAPPED）です。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "結果は、対応表とレコードから機械的に写した候補と照合の事実です。対応の意味の正しさ・データの移行の採用・適用の許可ではありません。採用と適用は Host・Runtime と人が行います。",
        "AI 由来の対応（ai_candidate）を事実にしません。converted は候補で、確定・適用ではありません。",
        "業務ごとの意味の対応表は持ちません。対応表は呼び出し側が渡します。",
        "レコードの値の個数の上限（record_nodes）は単体試験とファズのまれな生成だけで確かめ、expected.keys の上限（100,000 個）は試験していません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/map-reconcile.js の reconcileMap({ source_schema_id, target_schema_id, source_version, map, records, key_path, sum_paths?, expected? }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（converted・candidates_not_applied・conflicts・reconciliation・need_input など、inference: null・advice: null）か { ok: false, errors: [{ code, path[, limit] }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-03",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "差分・逆操作・競合の検査 — 差分・逆操作・競合検査の部品（Kernel）",
      "summary": "差分・逆操作・競合検査の部品（Kernel）は、JSON の文書について、2 つの版の差分（old つきの patch）、patch の逆、patch を当てた候補、目的（intent）ごとの取り消しの計画を出す単独の部品です。当てる前に基底の版の一致・保護された位置・前提の値を検め、取り消しでは後の別の変更との重なりと逆にできない変更を並べて隠しません。確定・保存はしません。inference と advice はいつも null です。",
      "problem": "「元に戻す」の裏の差分で、古い版に patch を当ててしまうこと、保護された位置を書き換えること、取り消しで後の別の変更を壊すこと、逆にできない変更を一部だけ逆にして済ませることを防ぎます。",
      "use_when": [
        "2 つの版の JSON の差分を、old つきの patch（add・remove・replace）として出したいとき（diff）。オブジェクトのキーは UTF-16 の符号単位の順に歩きます。",
        "patch の逆を出したいとき（invert）。逆にできない操作（old の無い remove・replace）は NOT_INVERTIBLE として全部並べ、そのときは逆の patch を出しません。",
        "patch を当てた候補を、確定せずに出したいとき（apply）。base_revision が document_revision と違えば STALE_BASE、保護された位置に触れれば PROTECTED_PATH として、当てる前に全部並べます。committed はいつも false です。",
        "目的（intent）ごとの取り消しの計画を出したいとき（plan_undo）。intent が同じ変更を新しい順に逆にしてつなぎ、後の取り消さない変更と重なる位置は LATER_CHANGE_OVERLAPS として並べます。",
        "AI がした intent の割り当て（intent_origin: ai_candidate）を事実にせず、candidate_intent_changes に分けたいとき。その変更は取り消しに入れず、残す変更として重なりの相手にします。",
        "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。外の境界・状態はありません。"
      ],
      "do_not_use_when": [
        "変更を確定・保存したいとき、版を採番したいとき。原子的な適用・保存・版の採番はこの部品の外の役目で、apply は候補を出すだけです。",
        "文字列の中の文字単位の差分が要るとき。文字列が違えば replace 1 つです（文書の文字の断片編集は P-09 の範囲）。",
        "配列の要素の挿入・移動を見つけたいとき。並べ合わせ（LCS など）はせず、先頭への挿入は後ろの要素の replace の列になります。",
        "RFC 6902 の JSON Patch として使いたいとき。move・copy・test と配列の末尾の \"-\" は扱わず、old を持つ点・add が既にあるキーに置かない点でも別のものです。",
        "自然文の目的を変更に対応づけさせたいとき。割り当ては呼び出し側が決め、AI がした割り当ては ai_candidate として渡します。",
        "外部への送信や決済の取り消しに使いたいとき。対象外です。",
        "意味の上での依存（ある変更が別の変更を前提にしていること）を見たいとき。位置の重なりでしか見ません（意味の依存は P-04 の範囲）。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が決まったキーだけを持つ素のオブジェクトでないとき（知らないキー・足りないキー・getter・列挙されないプロパティ・symbol のキー）INVALID_ARGUMENT、値を調べること自体が例外になるとき INSPECTION_FAILED、operation が 4 つのどれでもないとき UNSUPPORTED_OPERATION で止めます。",
        "文書・value・old が JSON の値でないとき（undefined・NaN・単独のサロゲート・__proto__ というキー・穴のある配列・配列の派生クラス・クラスのインスタンスなど）INVALID_VALUE で止めます。",
        "patch の op が move・copy・test のとき UNSUPPORTED_OP、patch の形が違うとき（add・remove の path が根）INVALID_PATCH、JSON Pointer の形が違うとき（__proto__ という token を含む）INVALID_POINTER で止めます。",
        "版が 1〜128 文字の整った文字列でないとき INVALID_REVISION、origin・intent_origin が語彙の外のとき INVALID_ORIGIN、変更の形が違うとき INVALID_CHANGE、同じ change_id が 2 つあるとき DUPLICATE_CHANGE_ID、上限（操作 10,000・深さ 64・値 200,000 個など）を超えたとき RESOURCE_LIMIT（limit つき）で止めます。",
        "apply では、STALE_BASE と PROTECTED_PATH（等しい・下・上・配列の詰め替えで動く位置）を当てる前に全部並べ、どちらかがあれば当てません。無ければ写しの上で順に当て、PRECONDITION_FAILED（old の不一致・add の位置に値がある）か PATH_NOT_FOUND（\"-\"・先頭の 0 を含む）の最初の 1 つで止めます。",
        "plan_undo では、履歴の途切れ（HISTORY_GAP）・現在の版との違い（STALE_BASE）・逆にできない変更（NOT_INVERTIBLE）・後の変更との重なり（LATER_CHANGE_OVERLAPS）を conflicts に並べ、1 つでもあれば undo_patch を出しません。取り消す変更が無ければ no_matching_change です。",
        "apply(base, diff(base, target)) の候補が target と等しく、apply(target, invert(diff(base, target))) の候補が base と等しいことを、単体試験とファズで確かめました。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "結果は差分・逆・候補・計画の事実の提示で、変更の確定・保存・取り消しの実行ではありません。どの変更をどの目的に属させるか、候補を採用するかは、この部品の外（利用者・Runtime）で決めます。",
        "配列の詰め替えの判定は token の形だけで行います。数字の形のキーを持つオブジェクトでも詰め替えとして扱い、多めに止めます。",
        "need_input は使いません。facts.state はいつも done で、止める理由は refusals・conflicts・not_invertible に並べます（このとき ok は true）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/patch-conflict.js の patchConflict({ operation, ... }) を呼びます。diff・invert・apply・planUndo を直接呼ぶこともできます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state: \"done\"・operation と操作ごとの事実、inference: null・advice: null）か { ok: false, errors: [{ code, path }] }（RESOURCE_LIMIT は limit も）です。履歴・版・保存は呼び出し側が持ちます。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-04",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "来歴・依存のグラフ — 来歴・依存グラフの部品（Kernel）",
      "summary": "来歴・依存グラフの部品（Kernel）は、版つきの node と辺（登録済み registered と AI の候補 ai_candidate）、変更の集合から、登録済みの辺だけで下流へ届く node（impacted、最短の経路つき）と、候補の辺を使わないと届かない node（candidate_impacted）を分けて出す単独の部品です。循環・無い node や版の違う辺・権限外の node・届かなかった node も事実として出します。inference と advice はいつも null です。",
      "problem": "変更の影響範囲は、表からは見えません。AI の候補の依存を登録済みの依存と混ぜて事実にすること、欠けた辺や版の違いを黙ってたどること、届かなかった node を「無関係」と扱うことを防ぎます。",
      "use_when": [
        "変更（changed）から、登録済みの辺だけで影響が届く node を、深さと最短の経路（path・edges）つきで出したいとき（analyzeProvenance）。同じ長さの経路は node の id の並びがいちばん小さいものを選びます。",
        "AI の候補の辺（ai_candidate）を使わないと届かない node を candidate_impacted に分け、経路の中の候補の辺の index も受け取りたいとき。impacted と candidate_impacted は重なりません。",
        "循環を、登録済みの辺だけの強連結成分（cycles）と、候補の辺で初めてできる・大きくなる成分（candidate_cycles）に分けて受け取りたいとき。",
        "無い node への辺（NODE_ABSENT）と版の違う辺（VERSION_MISMATCH）を missing として受け取り、それらをたどらずに state を need_input にしたいとき。",
        "権限外の node（allowed: false）について、届いても id だけを not_allowed に出し、kind や version を出さないようにしたいとき。到達はその node を通って続けます。",
        "探索の深さ（max_depth、1〜256、無ければ 256）を決め、深さで切ったことを depth_limit_hit（登録済みの探索と、候補を入れた探索の別）で知りたいとき。",
        "時計・乱数・環境の locale を使わず、node の並びやキーの並びに関係なく、同じ入力から byte 単位で同じ出力が要るとき。"
      ],
      "do_not_use_when": [
        "影響の有無や修正の要否を判断させたいとき。到達と欠けの事実を出すだけです。",
        "届かなかった node が無関係だと示したいとき。unreached は探索の中で届かなかったという事実で、無関係であることの証明ではありません（unreached_meaning: not_reached）。",
        "辺の種類（kind）ごとにたどり方を変えたいとき。kind は読まず、どの kind も同じにたどります。",
        "版の前後関係を見たいとき。from_version・to_version は文字列の一致だけを見ます。",
        "allowed の印が正しいか（権限の真正性）を確かめさせたいとき、権限外の node の中身を遮らせたいとき。どちらも Host の役目で、Host は権限外の node の中身を渡しません。",
        "辺の向きを部品に決めさせたいとき。辺 { from, to } は「from が変わると to に響く」の向きで渡します（来歴なら 元 → 派生）。",
        "Node.js v24.21.0 のほかの版で使うとき（NOT_VERIFIED）や、上限いっぱいの入力の時間とメモリーを前提にしたいとき。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーは測っていません。"
      ],
      "what_it_checks": [
        "入力が素のデータでないとき（getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ）、知らないキー・足りないキー・null・語彙の外の値（origin が registered・ai_candidate 以外など）・1〜128 文字でない id・単独のサロゲートがあるとき INVALID_INPUT で止めます。",
        "max_depth が 1〜256 の整数でないとき INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy）INSPECTION_FAILED、id が重なるとき DUPLICATE_ID で止めます。",
        "上限（nodes 100,000 件・edges 400,000 件・changed 100,000 件・出力の経路の node id の合計 4,000,000）を超えたら、どの上限かを limit に入れて RESOURCE_LIMIT で止めます。失敗を空の成功にしません。",
        "辺の from_version・to_version が書かれていて、その node の version と違えば VERSION_MISMATCH、端の node が無ければ NODE_ABSENT として missing に出します。その辺はたどらず、循環の計算にも入れません。",
        "版の違う変更の node は、変わったことを Host が述べた事実として始点にし、VERSION_MISMATCH も出します。変更された node 自身は impacted・candidate_impacted・unreached のどれにも入れません。",
        "止めるときは最初に見つけた 1 つだけを { code, path, limit? } で返します。path は JSON Pointer（入力全体は空文字）です。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。AI 由来（ai_candidate）のものを事実に変えません。",
        "結果は到達と欠けの事実の提示で、影響の有無・修正の要否の判断ではありません。影響の判断や修正の計画の代行でもありません。最終の権限・遮断・適用は Host と Runtime の役目です。",
        "max_depth で切った場合、登録済みの辺では深い位置にある node が、候補の辺の短い経路で candidate_impacted に入ることがあります（そのとき depth_limit_hit.registered は true です）。",
        "入出力の形は v1 で定めたもので、使う側の受け入れはまだ済んでいません。",
        "ファイル・通信を使わず、資格情報を持ちません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/provenance-graph.js の analyzeProvenance({ nodes, edges, changed, max_depth? }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state・impacted・candidate_impacted・cycles・candidate_cycles・missing・not_allowed・unreached・depth_limit_hit・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用・状態・外の接続・資格情報は無く、依存パッケージも無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-05",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "制約・割当・時間枠 — 制約・割当・時間枠の部品（Kernel）",
      "summary": "制約・割当・時間枠の部品（Kernel）は、整数の分で書いた工程（所要時間・不可分か・分けるときの最小・前提・資源・時間窓）と枠（始まり・終わり・資源）から、決まった順の上限つき探索で、割当の案・成り立たない理由・探索の上限に届いたことを事実として返す単独の部品です。infeasible は証明できたときだけで、上限で止まったときは search_limit と分けます。不明は推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "予定を組むときに、不可分の工程を短い枠へ縮めて入れてしまうこと、探索を途中で打ち切っただけなのに「成り立たない」と言ってしまうこと、最適だと証明できていない案を最適として出すこと、分からない所要時間や時間窓を推測で埋めることを防ぎます。",
      "use_when": [
        "工程（0〜200 件）と枠（0〜200 件）から、目的 objective に合わせて割当の案を出したいとき（solveSchedule）。目的は feasible（全部置ける案）・min_unassigned（外す工程の数の最小）・earliest_finish（全部置いたときのいちばん遅い終わりの最小）の 3 つです。",
        "不可分の工程（indivisible: true）を、1 つの枠に duration.min の長さのまま丸ごと置きたいとき。枠と時間窓の重なりが min より短ければ、その枠には入れません（20 分の不可分工程を 10 分の枠へ入れません）。",
        "分けられる工程を、どの部分も splittable_min 以上、合計がちょうど duration.min になるように分けて置きたいとき。同じ工程の部分は時間が重ならず、part は時間の順に 1 から振られます。",
        "前提（requires）・資源（resource）・時間窓（window）を守った割当が要るとき。前提の工程はこの工程のどの部分よりも前に終わり、前提の工程が置かれなければこの工程も置きません。資源は工程と枠の両方にあるときだけ一致を求めます。",
        "成り立たないことを、理由のコードつきで受け取りたいとき。静的な理由（NO_SLOTS・NO_SLOT_FOR_RESOURCE・WINDOW_EMPTY・INDIVISIBLE_TOO_LONG・CAPACITY_TOO_SMALL・PRECEDENCE_CYCLE）と、探索を尽くした印 SEARCH_EXHAUSTED を reasons に出します。",
        "探索の手数を max_nodes（1〜1,000,000）で区切り、上限に届いたことを search_limit_reached と status（search_limit）で、最適性を optimality（proven か not_proven）で区別して受け取りたいとき。",
        "AI 由来の工程（origin: ai_candidate）を含むかどうかを、割当の行の origin・ai_candidate_tasks・plan_includes_ai_candidate で分けて受け取りたいとき。",
        "時計・乱数・環境の locale を使わず、配列の並びやキーの並びに関係なく、同じ入力から byte 単位で同じ出力が要るとき。"
      ],
      "do_not_use_when": [
        "案を採るか、工程を分け直すか、所要時間が正しいかを判断させたいとき。結果は割当の案と成り立たない理由・探索の上限の事実で、採用・実行の判断ではありません。",
        "工程の分け方や所要時間を部品に決めさせたいとき。どちらも呼び出し側が決めて渡します。この部品は工程を作らず、所要時間を推測しません。",
        "同じ人の時間の重なる 2 つの枠に、違う工程を同時に置くことを止めさせたいとき。枠どうしは独立の受け皿として扱います。1 人の予定なら、呼び出し側が重ならない枠を渡します。",
        "工程の間の移動・準備の時間、暦・時間帯、分より細かい時刻を扱いたいとき。時刻と長さは 0〜100,000,000 の整数の分だけです。",
        "重み・優先・柔らかい制約・締切の遅れの最小などの目的が要るとき。目的は 3 つだけで、ほかは UNSUPPORTED_OBJECTIVE で止めます。min_unassigned は工程の数を数え、長さは数えません。",
        "min より長くかかる工程の余裕を取りたいとき。duration.max は形を検めるだけで、置く長さはいつも duration.min です。",
        "大きな問題を決まった時間で解きたいとき。探索は指数的で、大きな問題では search_limit になりえます。速さは約束しません。max_nodes に達すると必ず止まります。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が { tasks, slots, objective, search } の 4 つのキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED、素のデータでないとき（getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・symbol のキー・__proto__ のキー・列挙されないキー）NOT_PLAIN_DATA で止めます。素のデータに写してから検め、検めた値と探索に使う値は同じです。",
        "工程の形が違うとき（知らないキー・足りないキー・id・origin・indivisible の形、duration.min が 1 未満・max が min 未満・整数でない、不明の印の形の違い、不可分の工程の splittable_min・min を超える splittable_min、requires の重複・配列でない、時間窓の end が start 以下・負の値）INVALID_TASK で止めます。null は受け取りません。",
        "枠の形が違うとき（end が start 以下・整数でない・負など）INVALID_SLOT、search が { max_nodes } でないとき INVALID_SEARCH、objective が 3 つの目的のどれでもないとき UNSUPPORTED_OBJECTIVE、同じ id の工程か枠が 2 つあるとき DUPLICATE_ID、requires が無い工程の id を指すとき UNKNOWN_TASK_REF で止めます。",
        "上限（工程 200 件・枠 200 件・前提は工程ごとに 200 件・max_nodes 1,000,000・時刻と長さ 100,000,000 分・配列 4096 要素・入れ子 4 段）を超えたら、どの上限かを limit に入れて RESOURCE_LIMIT で止めます。止めるときは { code, path, limit? } を返し、path は JSON Pointer です。",
        "duration か window が不明の印 { unknown: true } のとき、推測せず state を need_input にし、足りないものを need_input に並べます。探索はせず、status・assignment・objective_value は null、reasons は空です（途中までの結果を出しません）。",
        "feasible と earliest_finish では、静的な理由が 1 つでもあれば探索せず infeasible にします（nodes_explored は 0）。上限に届いて案が無ければ infeasible と言わず search_limit にし、上限に届いた後に残った案は not_proven にします（search_limit_reached が true なら nodes_explored = max_nodes）。",
        "結果を、src の補助関数を使わずに規則から書き直した独立オラクル（小さな問題の総当たりと割当の検算を含む）と突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは割当の事実だけです。inference と advice はいつも null で、出力のどの文字列にも直し方や優先の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。AI 由来の工程（ai_candidate）を事実に変えず、正しいかも判断しません。",
        "結果は割当の案と成り立たない理由・探索の上限の事実の提示で、予定の判断や実行ではありません。案を採るか、工程を分け直すか、所要時間が正しいかは、この部品の外で利用者が決めます。",
        "探索が完全であること（全部置ける案があれば見つかること）は、左詰めの議論と、小さな問題の総当たりとの 1,000,000 回の突き合わせで確かめました。形式的な証明はしていません。",
        "総当たりの突き合わせは、工程 4 件・枠 3 件・時刻 20 前後までの小さな問題だけです。それより大きな問題の infeasible・proven は、総当たりでは確かめていません。",
        "nodes_explored はこの部品の決まった数え方で、ほかの解き方の手数とは比べられません。同じ値の案のどれを出すかは探索の順で決まります。",
        "入出力の形と手数の数え方は v1 で定めたもので、既存の実装はありません（新規）。",
        "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/constraint-schedule.js の solveSchedule({ tasks, slots, objective, search }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state・status・optimality・assignment・unassigned・reasons・need_input・search_limit_reached など、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用・状態・外の接続・資格情報は無く、依存パッケージも無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-06",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "モデル・限定ルールの評価 — モデル・限定ルール評価の部品（Kernel）",
      "summary": "モデル・限定ルール評価の部品（Kernel）は、呼び出し側が渡すモデル（JSON の木の式）と限定ルール（条件と効果だけの小さな DSL）を、決まった上限の中で評価する単独の部品です。evaluateModel は 10 進の BigInt 計算（精度 34 桁、half_even）で出力ごとの値と単位を出し、evaluateRules・runRules は役ごとの合法手・次の状態（候補）・終わりと結果を出します。繰り返し・再帰・コードの実行は受け取りません。UNKNOWN は推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "試算や判定の式を評価するときに、浮動小数点の誤差が値に入ること、0 での割り算や範囲外の結果が 0 や NaN として紛れ込むこと、欠けた入力を推測で埋めて途中までの計算を出してしまうこと、規則の評価が止まらない繰り返しやコードの実行になることを防ぎます。乱数も明示の seed からだけ作るので、同じ入力と seed から同じ結果を出します。",
      "use_when": [
        "モデル（params で引数と単位を宣言し、outputs に出力ごとの式を書いたもの）と inputs（値・単位・origin）から、出力ごとの値と正準の単位を 10 進の文字列で出したいとき。式の演算は + - * / min max neg、round（scale 0〜34、half_even・half_up・floor・ceil）、rand です。",
        "1 つの引数を sweep で 1〜1000 点動かし、点ごとの値を並べたいとき（点の数 × 節の数は 1,000,000 まで）。",
        "乱数を含む式を、明示の seed（0〜2^53−1 の整数）から再現できる形で評価したいとき。生成器は seed から SplitMix64 で 4 語を作る xorshift128 で、出力ごと・走査の点ごとに始め直します。",
        "単位の次元をそろえて計算したいとき。単位は記号ごとの指数の組で比べ、* と / は指数を足し引きし、+ - min max は次元がそろうことを求めます。",
        "役（roles）・手（moves の when と effects）・終わり（end）で書いた限定ルールと状態から、evaluateRules で役ごとの合法手、手を渡したときの次の状態（候補）か ILLEGAL_MOVE と成り立たなかった最初の条件、終わりと結果を出したいとき。",
        "手の列を runRules で max_steps（1〜1000）まで順に当て、当てられなかった最初の手で止めて、その前までの状態を final_state として受け取りたいとき。",
        "AI 由来（origin が ai_candidate）のモデル・入力・走査・規則を使ったかどうかを、basis（registered_only か includes_ai_candidate）と candidate_sources の位置で分けて受け取りたいとき。"
      ],
      "do_not_use_when": [
        "結果を実際の需要の予測として使いたいとき。結果は渡された式の計算です。モデルの式・規則・入力の値が正しいかは確かめません。",
        "単位の換算（例えば m と km）を部品にさせたいとき。同じ次元の別の単位は別の記号として扱います。係数を掛けるなら式に書きます。",
        "暗号用の乱数や、統計の検定を通った乱数が要るとき。xorshift128 は暗号用ではなく、統計の検定はしていません。",
        "引数のある手、同時の手番、隠れた情報の確率、時間切れを含む規則を扱うとき。限定ルールにはこれらがありません。",
        "繰り返し・再帰・式の評価・コードの実行を規則に書きたいとき。語彙の外は UNSUPPORTED_CONSTRUCT で止めます。",
        "状態に真偽値・null・小数を入れたいとき。状態の値は整数・文字列・配列・オブジェクト・UNKNOWN の印 { unknown: true } だけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・null・__proto__ のキーは受け取らず、素のデータに写してから検めます。",
        "入力の形が違うとき（知らないキー・足りないキー・語彙の外の origin・名前の形・重複・JSON Pointer の形など）INVALID_INPUT、10 進の文字列が決まった形でない・有効桁が 34 桁を超える・範囲（0 か 10^-100 ≤ |値| < 10^100）の外・\"-0\" のとき INVALID_DECIMAL、単位の文字列の形が違う・合わせた指数が 64 を超えるとき INVALID_UNIT で止めます。",
        "宣言の無い引数・役を指すとき UNKNOWN_NAME、足し算・引き算・min・max の単位の次元が違うときと入力・走査の単位が宣言と違う次元のとき UNIT_MISMATCH、式に rand があるのに seed が無いとき SEED_REQUIRED、式の演算子・丸めの方法・条件や効果の op が語彙の外のとき UNSUPPORTED_CONSTRUCT で止めます。",
        "上限を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。上限は、式の深さ 64・節 10,000・走査 1000 点・状態 256 KiB と深さ 64・文字列 4096 文字・効果の値 4 KiB・max_steps 1000 などです。手を当てた後の状態の大きさもここで確かめます。",
        "使う引数に欠け（MISSING）か UNKNOWN が 1 つでもあれば、推測せず state を need_input にし、points を空にします（途中までの計算を出しません）。0 での割り算と範囲外の結果は、その出力だけ DIVISION_BY_ZERO・OUT_OF_RANGE と起きた節の位置で示し、値を 0 や NaN にしません。",
        "手は 定義→役→終わり→条件→効果 の順に確かめ、合法でなければ ILLEGAL_MOVE と成り立たなかった最初の条件（または MOVE_NOT_DEFINED・ROLE_MISMATCH・ENDED）を返します。効果を当てられなければ EFFECT_FAILED とし、手全体を捨てます（next_state は null）。状態の UNKNOWN の奥を読む条件や効果は、合法とも違反とも言わず undetermined にし、印の位置を need_input に並べます。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは計算の事実だけです。inference と advice はいつも null で、出力のどの文字列にも判断の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "結果は渡された式と規則の評価の事実で、モデルや規則が正しいこと、実際の需要の予測、遊びとしての良し悪しの判断ではありません。需要の予測・遊びの設計・規則の判定の代行もしません。式・規則・入力の値が妥当かは、使う側で確かめます。",
        "コードに固有の式や遊びの規則を持ちません。式・単位・規則・状態はすべて入力から受け取ります。",
        "34 桁の丸めを節ごとに重ねたときの誤差の大きさは評価しません。数値の不安定さとして出すのは precision_rounded と OUT_OF_RANGE の印だけです。",
        "状態の上限（256 KiB・深さ 64）は 1 手を当て終えた後に確かめ、1 手の途中の大きさは見ません。",
        "時計・環境の locale・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/model-rule.js の evaluateModel({ model, inputs, sweep?, seed? })、evaluateRules({ rules, state, role, move?, max_steps? })、runRules({ rules, state, moves, max_steps }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（state が done か need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-07",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "根拠・主張・適用条件 — 根拠・主張・適用条件の部品（Kernel）",
      "summary": "根拠・主張・適用条件の部品（Kernel）は、主張（fact・testimony・inference・hypothesis の型と、出典の ref・version・locator つき）ごとに、成立条件が現在の条件に当てはまるかを match・mismatch・unknown の 3 つで返す単独の部品です。部分的な一致は match にしません。出典の版が現在の版と違えば stale とし、評価を unknown（SOURCE_CHANGED）にします。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "「その根拠は今も使えるか」を確かめるときに、一部の条件だけが合う主張を全面的に当てはまるとしてしまうこと、資料が更新されたのに前の評価を使い続けること、「不明と入力された」と「まだ入力されていない」を混ぜてしまうこと、推論や仮説を事実に格上げしてしまうことを防ぎます。",
      "use_when": [
        "主張（0〜10,000 件）ごとの条件（0〜50 件、op は eq・ne・in・lt・le・gt・ge）を、current.conditions の現在の値と比べ、applies を match・mismatch・unknown で出したいとき。偽の条件があれば mismatch、偽が無く不明・未入力・比べられないものがあれば unknown、すべて真なら match です。",
        "出典の版（source.version）を current.source_versions と比べ、source_status（current・changed・not_entered・unknown）と stale を主張ごとに出したいとき。出典が current でなければ applies は unknown です。",
        "{ \"unknown\": true } と入力された値と、キーが無い（未入力の）値を分けたいとき。未入力だけを need_input（CONDITION_NOT_ENTERED・SOURCE_VERSION_NOT_ENTERED・SUPPORT_NOT_ENTERED）に並べ、state を need_input にします。0・false・空の文字列は値として扱います。",
        "主張の型（type）と出どころ（origin）をそのまま保ち、AI 由来（ai_candidate）の主張を candidate_claims に分けて受け取りたいとき。",
        "supports で書いた主張どうしの支えの関係を、主張ごとに型つきの最短の鎖（id・type・origin・depth・chain）として並べ、循環を support_cycles と in_support_cycle で知りたいとき。循環は止めずに出します。",
        "applies が unknown や mismatch になった理由を、reasons（決まった順）と failed・unknown_keys・missing_keys・not_comparable_keys で主張ごとに受け取りたいとき。"
      ],
      "do_not_use_when": [
        "主張が正しいか、採用するかを判定させたいとき。match は渡された条件と現在の値の照合の結果で、主張が真実であることは示しません。主張の内容と出典の位置（locator）の正しさも確かめず、locator はそのまま返すだけです。",
        "小数・日付・範囲・文字列の大小・正規表現で条件を比べたいとき。比べるのは文字列・整数・真偽値の一致と整数の大小だけです（日付は呼び出し側が整数にして渡します）。",
        "出典の版の前後関係（どちらが新しいか）を見たいとき。新旧は版の文字列の一致だけで見ます。出典の現在の版は呼び出し側が確かめて渡し、この部品は資料を読みません。",
        "支えるものの結果で、支えられる主張の applies を変えたいとき。applies は自分の条件だけで決まります。",
        "支えの鎖をすべて列挙したいとき。出すのは最短の鎖 1 本だけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "入力の形が違うとき（素のデータでない・知らないキー・足りないキー・語彙の外の値・型が違う・null・id が 1〜128 文字でない・単独のサロゲート・集合に同じ値が 2 回）INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy など）INSPECTION_FAILED で止めます。",
        "getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ・null は受け取りません。読んだ値の写しだけで計算し、入力を書き換えません。",
        "id が重なるとき DUPLICATE_ID、条件の演算子が eq・ne・in・lt・le・gt・ge のどれでもないとき UNSUPPORTED_OPERATOR で止めます。in の値が 0 件か 65 件以上のとき、lt・le・gt・ge の値が安全な整数でないときも止めます。",
        "上限（claims 10,000 件・1 つの主張の conditions 50 件・in の値 64 件・supports 64 件・current.conditions と source_versions 各 10,000 個・出力の支えの鎖の id の合計 2,000,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
        "等しさは型も含めて厳密に見ます（\"5\" と 5、true と 1 は違う）。lt・le・gt・ge で現在の値が整数でないときは偽にせず not_comparable とし、applies を unknown にします。",
        "止めるときは、最初に見つけた 1 つだけを { code, path, limit? } で返します（path は JSON Pointer）。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
        "結果は条件の当てはまりと出典の版の事実の提示で、主張が正しいかの判断でも、採否の判断の代行でもありません。主張の真実性は約束せず、権利の認定もしません。最終の権限・遮断・適用は Host と Runtime の役目です。",
        "支えの鎖に fact があっても、inference・hypothesis を格上げしません。主張をまとめることもしません。",
        "条件のキーと値の意味（何をもって成立とするか）は決めません。呼び出し側が決めて渡します。",
        "入出力の形はこの部品の v1 で定めたもので、使う側の受け入れはまだです。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーの測定はしていません。",
        "時計・乱数・環境の locale・外の境界・状態・資格情報を持たず、ファイル・通信を使いません。副作用は NONE です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/evidence-claim.js の evaluateClaims({ claims, current }) を呼びます（例外を投げず、入力を書き換えません）。受け取るのは主張の一覧 claims と、現在の条件と出典の版 current です。返すのは { ok: true, errors: [], facts }（claims・candidate_claims・support_cycles・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-08",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "表示モデル・操作の変換 — 表示モデル・操作変換の部品（Kernel）",
      "summary": "表示モデル・操作変換の部品（Kernel）は、登録された表示契約（view_id・version・entity_kind・fields・commands。宣言だけ）に従い、共通の entity（id・version・kind・data）から欄ごとの表示モデルを作り（project）、型付きの操作 event を型付きの command の候補に変える（command）単独の部品です。データに無い欄・不明・型の違う値を空にせず印で出し、違反と不足は事実として返します。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "画面と処理のあいだの変換で、データに無い欄を空の値と取り違えること、表示の設定で編集できない欄を編集できるようにしてしまうこと、表示の定義にスクリプトや関数が混ざること、操作のたびに AI を呼ぶことを防ぎます。",
      "use_when": [
        "表示契約の fields（path・label_key・kind（text・number・enum・list・bool）・editable・range・enum・max_length）に従って、entity.data から欄ごとの view（value・value_state・editable）を作りたいとき。",
        "欄の値を value_state（present・missing・unknown・kind_mismatch・out_of_contract）で分けたいとき。データに無い欄は { missing: true }、{ unknown: true } は不明のまま、型の違う値は { kind_mismatch: true } として出し、空の文字列・0・false・空の一覧と区別できます。",
        "settings（hidden・order・editable）で欄を隠す・並べ替える・編集不可にしたいとき。隠した欄は view から除き、hidden に並べます。",
        "操作 event（event_type・event_id・path・value）を、(event_type, path) の組で選んだ command 定義（op は set・increment・select・toggle）に照らし、型付きの command（op・path・value・base_version・event_id・event_type）の候補に変えたいとき。base_version には渡した entity_version を写します。",
        "操作の違反（UNKNOWN_EVENT・NOT_EDITABLE・VALUE_KIND_MISMATCH・RANGE_VIOLATION）と、値が無い・不明の event の need_input（VALUE_ABSENT・VALUE_UNKNOWN）を、ok: false ではなく事実として受け取りたいとき。"
      ],
      "do_not_use_when": [
        "画面を描画したいとき、権限を判断したいとき、操作を適用したいとき。この部品は表示と操作の変換だけをします。描画は App、権限・基底版の照合・適用は Host（Runtime）の役目です。",
        "表示契約が登録済みか、版が正しいか、entity の版が今の版かを確かめたいとき。渡された契約と版を写して返すだけです。",
        "小数や 10 進の文字列の数を扱うとき、文字列以外の要素の一覧を扱うとき。number は安全な整数だけ、list は文字列の配列だけです。",
        "表示の文言の辞書や翻訳が要るとき。label_key は id の形だけを検め、文言は持ちません（App の役目）。",
        "同じ event_id の再送を見分けたいとき。重複は判定しません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null は受け取らず、素のデータに写してから検めます。",
        "どこかに関数があるとき、表示契約・設定に宣言に無いキーがあるとき SCRIPT_REFUSED で止めます。式やスクリプトを評価する経路はありません。",
        "表示契約の形が違うとき（kind・op の語彙の外、op と kind の組の違い、min > max、enum の空や重なり、max_length の範囲外、path や (event_type, path) の重なり、command の path が欄に無いなど）INVALID_CONTRACT で止めます。command の制約 allowed で欄の enum を広げることもここで止めます。",
        "entity の形が違うとき INVALID_ENTITY、entity.kind が契約の entity_kind と違うとき ENTITY_KIND_MISMATCH、設定の形が違うとき INVALID_SETTINGS、設定が契約に無い path を名指しするとき SETTINGS_PATH_UNKNOWN、event の形が違うとき INVALID_EVENT で止めます。",
        "契約が編集不可の欄を設定で編集可にしようとしたとき EDITABLE_WIDEN_REFUSED で止めます。表示の違いで権限を増やしません。command は契約の editable だけを見ます。",
        "command では、組が無ければ UNKNOWN_EVENT、欄が編集不可なら NOT_EDITABLE、値の型が合わない・toggle に値があるとき VALUE_KIND_MISMATCH、範囲を外れれば RANGE_VIOLATION を返します。値は欄の制約と command の制約の両方を満たすときだけ通します。",
        "上限（入れ子 32 段・値の数 200,000・文字列 65,536・配列 10,000 要素・欄 256・command 256・enum と allowed 各 256）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは変換の事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。AI を呼ばず、時計・乱数・ファイル・通信も使いません。",
        "command は候補で、適用ではありません。適用・基底版の照合・権限の真正性・同時更新の競合は Host（Runtime）の役目です。",
        "increment の適用後の値が欄の range に入るかは確かめません。基底版の値を持たないため、command の range で増減の幅だけを限ります。",
        "out_of_contract の値は隠さずそのまま写して出します。kind_mismatch の値は出しません。",
        "画面・描画エンジン・権限の仕組みではありません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/view-model.js の project({ contract, entity, settings? }) と command({ contract, entity_version, event }) を呼びます（どちらも例外を投げず、入力を書き換えません）。返すのは { ok: true, errors: [], facts }（project は view・hidden、command は command・violation・need_input。inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-09",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "文書のアンカー・断片の編集 — 文書アンカー・断片編集の部品（Kernel）",
      "summary": "文書アンカー・断片編集の部品（Kernel）は、ブロックに分けた文書の中の位置（アンカー）を改版後の文書で探し直し（exact・relocated・ambiguous・lost）、断片の編集の差分と新しいブロックの文を候補として出す単独の部品です。位置は UTF-16 の符号単位で数え、サロゲートの組の間を位置にしません。候補が 2 か所以上なら全部並べて選びません。保護範囲に重なる編集は PROTECTED_RANGE として差分を出しません。inference と advice はいつも null です。",
      "problem": "コメントや参照の位置が、文書の改版でずれる事故を防ぎます。改版後に曖昧になった位置を勝手に 1 つに決めないこと、絵文字などのサロゲートの組を割った位置を作らないこと、保護された文字に触れる編集を差分にしないこと、消した文字を失わないことを、決まった規則で行います。",
      "use_when": [
        "ある版で作ったアンカー（doc_version・block_id・start・end・quote・prefix・suffix）を、今の文書（version・blocks）の中で探し直し、status（exact・relocated・ambiguous・lost）と location を受け取りたいとき（operation: resolve）。",
        "探し直しを決まった段の順で行いたいとき。exact（同じ版・位置の文字が quote と同じ）→ 前後つき（prefix+quote+suffix）が同じブロック → 前後つきが全部のブロック → quote だけが同じブロック → quote だけが全部のブロック の順で、一致が 1 つでもあった最初の段で決めます。",
        "曖昧な位置を確定させたくないとき。一致が 2 つ以上なら ambiguous とし、location を null にして候補を全部並べます（一覧は 1,000 件まで、candidate_count は全部の数、candidates_truncated で切ったことを示します）。",
        "ブロックの [start, end) を replacement に置き換える断片の編集から、diff（消した文字 removed と入れた文字 inserted を含む）と new_block_text を候補として受け取りたいとき（operation: edit）。replacement の出どころ（registered・human・ai_candidate）は replacement_origin として返します。",
        "呼び出し側が決めた保護範囲（protected）に重なる編集を止めたいとき。重なれば status を refused とし、violations に PROTECTED_RANGE を並べ、diff と new_block_text を null にします。"
      ],
      "do_not_use_when": [
        "置き換えた後に前後の文の意味が保たれるかを判定させたいとき。見るのは文字の保護だけで、meaning_checked はいつも false です。",
        "Unicode の正規化（NFC・NFKC）、大文字・小文字・空白の違いを同じとみなしたいとき、似た文をあいまいに探し直したいとき。比べるのは符号単位だけです。",
        "書記素（結合文字・絵文字の ZWJ 列・異体字セレクタ）の境目を守りたいとき。割らないのはサロゲートの組だけです。",
        "ブロックをまたぐアンカーや編集を扱うとき、ブロックの分割・結合を追いかけたいとき。",
        "文書の保存、版の採番、共同編集の競合の処理、出典（どの資料のどの版か）の保持が要るとき。この部品はアンカーと版を返すだけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が決まったキーだけを持つ素のオブジェクトでないとき（知らないキー・足りないキー・getter・列挙されないプロパティ・symbol のキー）INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED、operation が resolve・edit のどちらでもないとき UNSUPPORTED_OPERATION で止めます。",
        "文書の形が違うとき（単独のサロゲートを含む文など）INVALID_DOCUMENT、同じ id のブロックが 2 つあるとき DUPLICATE_BLOCK_ID、アンカーの形が違うとき（start > end、quote の長さが end − start でない、prefix・suffix が 32 符号単位を超えるなど）INVALID_ANCHOR で止めます。",
        "編集の形が違うとき INVALID_EDIT、origin が語彙の外のとき INVALID_ORIGIN、保護範囲の形が違う・空・文書に無いブロック・長さを超える・組の間のとき INVALID_PROTECTED、編集する block_id が無いとき BLOCK_NOT_FOUND、end が文の長さを超えるとき INVALID_RANGE、start か end が組の間のとき SURROGATE_SPLIT で止めます。",
        "上限を超えたとき RESOURCE_LIMIT で止め、limit に blocks（10,000）・document_bytes（文の UTF-8 の合計 1,048,576 バイト。編集の後の文書も）・protected（1,000）のどれかを入れます。",
        "消す範囲 [start, end) が保護範囲と 1 文字でも重なるとき、または挿入（start = end）が保護範囲の内側（両端を除く）にあるとき、PROTECTED_RANGE を violations に並べ、差分を出しません（ok は true）。",
        "一致は重なりも数え（\"aaa\" の中の \"aa\" は 2 か所）、ブロックをまたいだ一致はしません。出力の位置にも、編集・保護の位置にも、サロゲートの組の間を使いません。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null で、resolve は文書の文を出しません。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
        "edit は候補を出すだけで、文書を確定・保存しません（committed はいつも false）。ambiguous の候補からどれを選ぶか、編集を採用するかは、この部品の外（利用者・Runtime）で決めます。",
        "結果は位置の候補と編集の候補の事実の提示で、位置の確定でも、意味が保たれることの約束でもありません。位置の確定や文書の編集の代行もしません。",
        "保護範囲は作りません。どこを保護するかは呼び出し側が決めて渡します。",
        "W3C Web Annotation の TextQuoteSelector・TextPositionSelector に近い形ですが、それに準拠するとは言いません（位置は UTF-16 の符号単位で、ブロックの id を持ちます）。",
        "時計・乱数・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/document-anchor.js の documentAnchor({ operation: \"resolve\" | \"edit\", ... }) を呼びます（resolve・edit を直接も呼べます。例外を投げません）。resolve は { document, anchor }、edit は { document, block_id, start, end, replacement, origin, protected } を受け取ります。返すのは { ok: true, errors: [], facts }（state は done、inference・advice は null）か { ok: false, errors: [{ code, path }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-10",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "イベントの集計・状態の還元 — イベント集計・状態還元の部品（Kernel）",
      "summary": "イベント集計・状態還元の部品（Kernel）は、宣言だけの reducer（event の種類ごとに set・add・append・remove の規則）で、版つきの state に events を配列の順に当て、新しい状態の候補（new_state）と、当てた event・重複・順序違反・区切り（window）の外・当てられなかったもの・種類ごとの件数を事実として出す単独の部品です。同じ event_id の再送は当てません。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "履歴から今の状態を作るときに、再送された event で数が二重に増えること、seq の飛びや逆行を見逃して状態を作ってしまうこと、区切りの途中で規則が替わること、1 つの event の規則が途中まで当たった中途半端な状態が残ること、無い値を 0 として足してしまうことを防ぎます。",
      "use_when": [
        "reducer（version と、種類ごとに 0〜64 件の規則 { op, path, from（\"payload.<キー>\"）か value }）で、state に events（0〜10,000 件の { event_id, seq, at, type, payload }）を順に当て、new_state の候補と aggregates.by_type（当てた event の種類ごとの件数）を受け取りたいとき。",
        "同じ event_id の再送を当てたくないとき。seen_event_ids にあれば duplicates（source: seen）、同じ束で 2 度目なら duplicates（source: batch）に並べ、増加は 0 です。",
        "seq の連続を確かめたいとき。直前の seq + 1 だけを受け取り、飛び（gap）は need_input（SEQ_GAP）、逆行（reversal）は order_violations に並べ、どちらもその後の event を not_applied にします。last_seq で最初に期待する seq を決められます。",
        "window（start・end・reducer_version）で区切りを決め、start ≤ at < end の外の event を outside_window にしたいとき（当てませんが seq は進めます）。",
        "1 つの event の規則を全部当てるか全く当てないかで扱いたいとき。当てられなければ、その event で変えたものをすべて戻し、apply_failures に event_id・rule_index・code を並べ、その後の event を not_applied にします。"
      ],
      "do_not_use_when": [
        "イベントの台帳・配信基盤・データベースとして使いたいとき。保存・確定・配信、重複の台帳・時刻・lease・永続化は Host（Runtime）の役目です。",
        "同じ event_id で中身の違う event を見分けたいとき。重複は event_id だけで判定し、中身は比べません。",
        "events を seq で並べ替えてから当てたいとき。配列の順に当てます。並べ替えが要るときは、渡す前に呼び出し側で行います。",
        "10 進の小数を足したいとき。add は安全な整数どうしだけです。",
        "配列の添字への set や、一覧の中の差し替えが要るとき。set は親がオブジェクトのときだけです。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null・関数は受け取らず、素のデータに写してから当てます。",
        "reducer の形が違うとき（知らないキー・op の語彙の外・path の形・__proto__ の token・from と value の数・from の形・add の value が整数でないなど）INVALID_REDUCER で止めます。reducer は宣言だけで、式・スクリプト・関数を評価する経路はありません。",
        "state の形が違うとき INVALID_STATE、events の形が違うとき（seq と at が 0 以上の整数でないなど）INVALID_EVENTS、seen_event_ids が id の一覧でないとき INVALID_SEEN、window の形が違うとき（start > end など）INVALID_WINDOW で止めます。",
        "window の reducer_version が reducer.version と違うとき RULE_SWITCH_IN_WINDOW で止めます。区切りの途中で規則を切り替えません。",
        "規則を当てられない理由を code で返します。種類の規則が無い NO_RULE_FOR_TYPE、payload にキーが無い PAYLOAD_KEY_MISSING、親がオブジェクトでない PARENT_NOT_OBJECT、対象が無い・整数でない・一覧でない TARGET_MISSING・TARGET_NOT_INTEGER・TARGET_NOT_LIST、値が整数でない VALUE_NOT_INTEGER、和があふれる INTEGER_OVERFLOW です。無い値・不明・文字列を 0 として扱いません。",
        "上限（入れ子 32 段・値の数 400,000・文字列 65,536・配列 100,000 要素・events 10,000・種類 256・種類ごとの規則 64・append 後の一覧 10,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
        "new_state は候補で、確定ではありません。保存・配信・適用の確定は Host の役目です。",
        "seen_event_ids が本当に当てた event か、state の版が今の版か、at が正しい時刻かは確かめません。どれも Host が渡します。時計は読まず、at と window は入力の値だけを使います。",
        "同じ seq の別の event（event_id が違う）は逆行として止めます。どちらが正しいかは決めません。",
        "値つきの remove の等しさは、キーの集まりと値の一致で見ます（数の -0 と 0 は等しい）。",
        "時計・乱数・ファイル・通信・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/event-reduce.js の reduceEvents({ state, state_version, reducer, events, seen_event_ids, window?, last_seq? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（new_state・applied・duplicates・order_violations・apply_failures・not_applied・aggregates・need_input など。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-11",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "構成・互換・試験計画の検査 — 構成 DAG・互換・試験計画検査の部品（Kernel）",
      "summary": "構成 DAG・互換・試験計画検査の部品（Kernel）は、部品の ToolLock の一覧（tool_id・contract_version・artifact_digest・ports・capabilities）と接続の辺、許された能力、fixture 案から、構成が接続できるか（connectable）と問題（重複・能力の不足・port の不在・版や向き・schema・単位の不一致・必須 port の未接続・循環）、adapter の要る辺、試験計画を事実として出す単独の部品です。AI が作った期待値は確定に数えません。inference と advice はいつも null です。",
      "problem": "部品を組んだときの不整合は、動かすまで見えません。この部品は、接続の辺の向き・版・schema・単位の食い違い、つながっていない必須の port、循環、許されていない能力を、動かす前に一覧にします。AI が作った期待値を確定した試験として数えてしまうこと、lock が無いのに「接続できる」と答えてしまうことも防ぎます。",
      "use_when": [
        "locks（0〜1,000 件）と edges（0〜10,000 件の { from: { tool_id, port, contract_version? }, to: 同じ }）から、connectable と problems を受け取りたいとき。problems が 0 件のときだけ connectable は true です。",
        "各 lock の capabilities を allowed_capabilities と照らし、許されていない能力を CAPABILITY_NOT_ALLOWED として並べたいとき。",
        "辺の両端の schema_digest が違うところを、adapter の要る辺（adapters_needed）として知りたいとき。adapter は作りません。",
        "試験計画を道具の位相順（入次数 0 の道具のうち tool_id の小さいものから）で受け取りたいとき。循環とその先で並べられない道具は unordered に並べます。",
        "fixtures（id・tool_id・input_digest・expected_digest・origin）を、道具ごとに確定（human・known_source）・未確定（ai_candidate）・食い違い（conflicting）に分けたいとき。確定の fixture の無い道具と、lock に無い道具の fixture（orphan_fixtures）も並べます。"
      ],
      "do_not_use_when": [
        "lock の digest が本物の artifact と一致するか、署名、供給者の権限、利用側の受け入れを確かめたいとき。形だけを検め、値を比べます。これらは Host・Runtime などで別に確かめます。",
        "schema の中身や互換の幅を比べたいとき。schema_digest が同じなら同じ型とみなし、違えば adapter が要るとだけ言います。",
        "単位の換算の可否や、同じ意味の別表記を判断させたいとき。単位は文字列の完全一致だけで見ます。",
        "fixture の期待値が正しいか、known_source の根拠そのものが確かかを判断させたいとき。origin を写して分けるだけです。",
        "1 つの in の port に 2 本以上の辺がつながること（fan-in）や、同じ辺の重なりを問題として出したいとき。これらは問題として出しません。contract_version の新旧の順序も判断しません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が locks・edges・allowed_capabilities・fixtures の 4 つだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null・関数は受け取らず、素のデータに写してから検めます。",
        "lock の形が違うとき（知らないキー、digest が sha256:<64 桁の小文字の 16 進> でない、direction の語彙の外、unit の形、port の名前や能力の重なりなど）INVALID_LOCKS で止めます。authorized などのキーは受け取らず、authorized=true で根拠を代わりにしません。",
        "辺の形が違うとき INVALID_EDGES、allowed_capabilities が id の一覧でないとき INVALID_CAPABILITIES、fixture の形が違うとき（origin の語彙の外・id の重なり・余分なキーなど）INVALID_FIXTURES で止めます。",
        "上限（入れ子 16 段・値の数 400,000・文字列 4,096・配列 100,000 要素・locks 1,000・lock ごとの port 64 と能力 64・allowed_capabilities 1,000・edges 10,000・fixtures 10,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
        "辺の端ごとに、道具か port が無ければ PORT_UNKNOWN、端の contract_version が lock と違えば VERSION_MISMATCH、向きが違えば（from が out、to が in でない）DIRECTION_MISMATCH を出します。両端がつながれば schema_digest の違いを SCHEMA_MISMATCH、単位の違い（不明・片側だけを含む）を UNIT_MISMATCH として出します。",
        "同じ tool_id の lock を DUPLICATE_TOOL、つながる辺の無い required の port を REQUIRED_PORT_UNCONNECTED、道具の間の矢の強連結成分（2 つ以上か自己ループ）を CYCLE として出します。lock が 0 件なら connectable を null とし、need_input（NO_LOCKS）にします。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
        "結果は構成の事実の検めで、採用・署名・発行・実行の許可ではありません。pack の発行・署名・実行環境でもありません。",
        "署名・発行・adapter の生成はしません。時計・乱数・ファイル・通信も使いません。",
        "AI の期待値（ai_candidate）は unconfirmed に並べ、確定に数えません。多数決や confidence の欄は受け取りません。確定どうしで同じ入力に違う期待値があれば、その fixture をすべて conflicting に分け、確定に数えません。",
        "ToolLock の repository・source_revision・source_path・license・publisher_evidence_ref・consumer_acceptance_ref・input_schema_digest・output_schema_digest は、形だけを検めて使いません。",
        "外の境界・状態を持たず、副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/assembly-check.js の checkAssembly({ locks, edges, allowed_capabilities, fixtures }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（connectable・problems・adapters_needed・test_plan・expectation_conflicts・tools_without_confirmed_fixture・orphan_fixtures・need_input。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-12",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "役割ごとの文脈・使わない項目 — 役割別文脈・非使用集合の部品（Kernel）",
      "summary": "役割別文脈・非使用集合の部品（Kernel）は、役割（allowed_scopes つき）ごとに、渡してよい項目の集合（projection）と、除いた項目とその理由、必要なのに入らない項目（missing_required）を出す単独の部品です。除外は derived_from を推移的にたどります。非使用の推移的な集合（non_use_closure）は参照を止める候補として出し、削除したとは言いません（deleted: false）。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
      "problem": "役割に渡してはいけない情報を除くときに、使わないと決めた項目から作られた項目（派生した項目）を見落とすこと、出どころを確かめられない項目を渡してしまうこと、参照を止める候補を「削除した」と取り違えることを防ぎます。",
      "use_when": [
        "roles（0〜50 件の { role, allowed_scopes }）と items（0〜100,000 件の { id, scope, derived_from, kind }）と non_use から、役割ごとの projection と excluded を受け取りたいとき。projection と excluded で、役割ごとに項目を漏れなく重ならずに分けます。",
        "除いた理由を決まった順で全部受け取りたいとき。NON_USE → SCOPE → DERIVED_FROM_NON_USE → DERIVED_FROM_OUT_OF_SCOPE → DERIVED_FROM_NOT_ENTERED の順に、当たるものを reasons に並べ、先頭を reason にします。",
        "非使用の推移的な集合を、参照を止める候補として受け取りたいとき。non_use_closure は non_use の id と、祖先に non_use の id がある項目で、meaning は reference_stop_candidates、deleted はいつも false です。non_use にあって項目に無い id は non_use_not_in_items に出します。",
        "required（役割ごとの必要な項目の id）を渡し、projection に入らない項目とその理由を missing_required として知りたいとき。これは projection についての事実で、need_input にはしません。",
        "derived_from が入力に無い id を名指すとき、その id を items_not_entered と need_input（ITEM_NOT_ENTERED）に並べて知りたいとき。state は need_input になり、その項目は除外のままです。"
      ],
      "do_not_use_when": [
        "権限の判断・入力前の遮断・実際の参照停止や削除をさせたいとき。最終の権限は Runtime、入力前の遮断と実際の参照停止・削除は Host の役目です。この部品は渡された一覧から集合を計算するだけです。",
        "allowed_scopes や scope が正しいか（権限の真正性）を確かめたいとき。範囲の語彙は呼び出し側が決めて渡し、この部品は確かめません。",
        "方針の内容や旧方針との差を扱いたいとき。policy_version は形を検めて写すだけです。",
        "項目の種類（kind）ごとに扱いを変えたいとき、どの祖先のせいで除いたかを知りたいとき。kind は計算に使わず、理由は種類だけを出します。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "入力の形が違うとき（素のデータでない・知らないキー・足りないキー・語彙の外の値・型が違う・null・id が 1〜128 文字でない・単独のサロゲート・集合に同じ値が 2 回）INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy など）INSPECTION_FAILED で止めます。",
        "getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ・null は受け取りません。読んだ値の写しだけで計算し、入力を書き換えません。",
        "役割の名前や項目の id が重なるとき DUPLICATE_ID、required が roles に無い役割を名指すとき UNKNOWN_ROLE、derived_from が循環するとき（自分を名指す項目を含む）DERIVATION_CYCLE で止めます。循環の位置は、循環の上にある項目のうち index のいちばん小さいもの（/items/<index>）です。",
        "上限（roles 50 件・allowed_scopes 1,000 件・items 100,000 件・1 つの項目の derived_from 1,000 件・derived_from の合計 1,000,000・non_use 100,000 件・required の役割 50 個と 1 つの役割の id 100,000 件）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
        "除外は祖先の祖先までたどります。祖先に入力に無い id がある項目（確かめられない項目）は projection に入れません。",
        "止めるときは、最初に見つけた 1 つだけを { code, path, limit? } で返します（path は JSON Pointer）。",
        "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
        "何も削除しません。non_use_closure は参照を止める候補で、削除の記録ではありません。実際の参照停止・削除・削除の失敗は、この部品の外で起きます。",
        "権限の付与・遮断・削除の代行はしません。最終の権限・遮断・適用は Host と Runtime の役目です。",
        "非使用の記録や参照の停止を保存しません（状態を持ちません）。",
        "入出力の形はこの部品の v1 で定めたもので、使う側の受け入れはまだです。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーの測定はしていません。",
        "時計・乱数・外の境界・資格情報を持たず、ファイル・通信を使いません。副作用は NONE です。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "src/role-context.js の projectRoleContext({ roles, items, non_use, policy_version, required? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（役割ごとの projection・excluded・missing_required と、non_use_closure・non_use_not_in_items・items_not_entered・need_input。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-13",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "内容・版・差分の適用 — 内容・版・差分適用の継手（Tsugite）",
      "summary": "内容・版・差分適用の継手（Tsugite）は、Kernel の型付き差分（typed-patch/v1）を、Runtime の店の 1 つの内容へ、楽観的な版（expected_revision）のもとで適用（APPLY）するか、書かずに確かめる（CHECK）ために運び、返った receipt（ContentPatchApplyReceipt）を契約どおりか検める継手（つぎて）です。店は Runtime のもので、この継手は店を作りません（fail-closed）。",
      "problem": "保存の裏で、別の人の変更を上書きしないようにします。差分を版と利用者の確認に結んで運び、受け取りと適用と不明を取り違えず、二重に適用しません。",
      "use_when": [
        "内容への変更を、基の版（expected_revision）が今の版と同じときだけ適用したいとき。版が違えば CONFLICT で、何も書きません。",
        "差分を書かずに、適用できるかだけ確かめたいとき（op CHECK、効果 NONE）。",
        "AI の案を、利用者本人がその案と差分を確かめた後だけ、採用（ADOPTED_CANDIDATE）として運びたいとき。確認は client.recordConfirmation で受け取ります。",
        "結果の分からない適用を、同じ要求の再送で解き、二重に適用しないようにしたいとき。"
      ],
      "do_not_use_when": [
        "店や版の管理そのものがほしいとき。原子的な適用・版・権限は Runtime の役目で、この継手は店を作りません。",
        "差分を作りたい・差分の中身の正しさや逆操作を確かめたいとき。それは Kernel と Runtime の役目です。",
        "CONFLICT のあとに、自動で rebase や再試行をさせたいとき。この継手は黙って rebase も再試行もしません。",
        "すぐに使える接続先がほしいとき。実の Runtime への適用は 0 回で、port は試験の代役だけです。port を実装するための Runtime 側のコマンドは、まだ設計だけです。",
        "業務の規則を組みたいとき。この継手は Kernel とデータ・サービスをつなぐアプリ基盤の継手で、業務の規則は部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "契約の版を検めます。要求の contract_pin が設定 contract_schema_digest と違うときは送りません。設定が無ければ何も送りません。",
        "作業場は設定 workspace_ids で許したものだけへ送ります。内容の hash を知っていることを権限として扱いません。",
        "利用者の確認を検めます。ADOPTED_CANDIDATE は、その案と差分（patch_digest の値）に結んだ利用者本人（origin HUMAN）の確認があるときだけ送ります。確認は照合できた APPLY の答えで使い切ります。",
        "受け取りを照合します。received の patch_digest が依頼とまったく同じで、base_revision が expected_revision と同じときだけ受け取ります。APPLIED は new_revision が expected_revision + 1 で、新しい内容の digest があるときだけです。CONFLICT は何も書いていないときだけです。",
        "受け取りの contract_pin が依頼と同じときだけ、普通の答えとして受け取ります。別の版は INCOMPATIBLE_VERSION の断りだけで、同じ pin の INCOMPATIBLE_VERSION は異常として停止します。",
        "結果の分からない APPLY（port の障害、UNKNOWN の receipt）は、同じ要求の再送で解きます（port.lookup があれば先に記録を読みます）。解けるまでほかの要求は送らず、同じ request_id で別の中身も送りません。",
        "ERABAZU の店を名乗る receipt、効果の増えた・分からない receipt、別の要求・別の作業場の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。APPLY の効果は LOCAL_REVERSIBLE、CHECK は NONE です。"
      ],
      "what_it_does_not_do": [
        "差分の bytes が patch_digest の値になること、target.content_digest が基の版の内容の digest であること、新しい内容の digest が適用後の内容の digest であることは確かめません（Runtime の役目）。",
        "権限（REF_NOT_PERMITTED）・別の作業場（WORKSPACE_MISMATCH）・保護範囲（PROTECTED_RANGE）の判定はしません。Runtime が判定し、この継手はその答えを返します。",
        "二重に適用しないことは、Runtime の重複除きに頼ります。試験では代役がそのとおりに振る舞うことだけを確かめました。",
        "利用者の確認の記録・照合済みの記録はこの継手の中だけで、再起動で消えます。違う版を名乗る port が本当にその版を持っているかは確かめません。",
        "適用できたことは承認ではなく、内容が正しいことでも採用の承認でもありません（どの結果にも approval: false が付きます）。",
        "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/content-patch-apply/v1（ContentPatchApplyRequest / ContentPatchApplyReceipt）でつなぎます。createContentPatchApplyClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Runtime の port（submit と任意の lookup）と cfg（contract_schema_digest・workspace_ids）。副作用: APPLY は LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-14",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "ファイルの入出力 — ファイル入出力の継手（Tsugite）",
      "summary": "ファイル入出力の継手（Tsugite）は、App で利用者が選んだファイルの handle を使って、CSV（RFC 4180・UTF-8）と JSON の取込の下見（IMPORT_PREVIEW）と書き出し（EXPORT）の依頼（FileIoRequest）を Host へ運び、返った receipt（FileIoReceipt）を契約で照合する継手（つぎて）です。下見を採用として扱わず、書き出した bytes は継手が自分で読み直して確かめます（fail-closed）。",
      "problem": "利用者が選んでいないパスや URL を読み書きしてしまうこと、取込の下見をそのまま採用と取り違えること、そして式に見える欄（= + - @ TAB CR で始まる欄）を残した CSV を書き出してしまうことを防ぎます。",
      "use_when": [
        "利用者が選んだ CSV・JSON のファイルを、取り込む前に下見したいとき。下見で受け取るのは、件数・位置と code だけの issues・digest です。",
        "CSV・JSON を新しいファイルとして書き出し、書いた bytes の長さ・sha256・欄の囲み・式への対策を、継手に読み直して確かめさせたいとき。",
        "扱う op・形式・上限・mapping を、設定で pin した範囲に限りたいとき。",
        "結果の分からない書き出しを自動で送り直さず、App が出力を確かめるまで次の書き出しを止めたいとき。"
      ],
      "do_not_use_when": [
        "取込の結果をそのまま採用（反映）したいとき。下見は採用ではなく（adopted: false）、採用は内容・版・差分適用の継手の役目です。",
        "変換の正しさ・件数の正しさ・issues の位置の正しさを確かめたいとき。それは変換・照合の部品（Kernel）と App の役目です。",
        "CSV（RFC 4180）・JSON 以外の形式や、UTF-8 以外の文字コードを扱いたいとき。",
        "既にあるファイルを上書きしたいとき。書き出しは新しいファイルだけです。",
        "パスや URL を直接指定して読み書きしたいとき。この継手は、App が admitHandle で渡した handle だけを使います。",
        "すぐに使える Host がほしいとき。実の Host・ファイルには一度も接続しておらず、port の実装・handle の出し方・設定の値は接続するときに別の関門で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定を検めます。allowed_ops・allowed_media_types に無い依頼、上限の天井（limits_ceiling。契約の 50 MiB・1,000,000 行・1024 列以下）を超える依頼、pin していない mapping、宣言した拡張子が形式に合わない依頼（.json の CSV など）は、Host に送りません。空の一覧は何も許しません。",
        "契約の版を検めます。依頼の contract_pin が設定の contract_schema_digest と違えば送りません。受け取りが名乗る版が依頼と違えば、INCOMPATIBLE_VERSION の REJECTED のほかは受け取りません。",
        "handle を検めます。App が渡していない handle、向きの違う使い方（取込元の handle を出力に使うなど）、URL・Windows のパス、一度書き出した出力の handle は使いません。",
        "依頼の形を検めます。dialect の無い CSV、dialect や式への対策のある JSON、式への対策の無い CSV の書き出し、op の欄の混在、key_fields のある書き出し、上書きを許す書き出しは送りません。同じ request_id の違う依頼も送りません（REFUSED）。",
        "receipt を照合します。request_digest の偽り、別の op の receipt、原文を変えたとする receipt、効果を増やす・不明な効果、op に合わない理由、件数の合わない receipt（rows_valid + rows_invalid が rows_read にならない）、欄の値や原文を含む issues は受理せず、停止します（HALTED）。",
        "issues の位置と順を検めます。行の範囲（CSV は 0〜rows_read、JSON は 1〜rows_read）、形式ごとの位置の欄（CSV は column、JSON は json_pointer）、code ごとの位置の有無、原文の順（同じ行では行全体の issue が先）に合わない receipt は受理しません。",
        "書き出しの後、Host が読み直した bytes（readOutput）を継手が自分で読みます。長さ・sha256 が receipt と違う、UTF-8 でない、CSV の欄が囲まれていない・式に見える欄が残る・見出しがちょうど 1 行でない、JSON がオブジェクトの配列 1 つでない、行や欄の数が合わない、付けた数（cells_escaped）を多く言うときは受理しません。",
        "Host の障害・UNKNOWN・FAILED・読み直せない出力のあとは、書き出しを自動で送り直しません。lookup で尋ねるだけで、App が outputChecked を呼ぶまで次の書き出しはしません（取込は読むだけなので続けられます）。"
      ],
      "what_it_does_not_do": [
        "下見は採用ではなく、書き出せたことは中身が正しいことではありません（どの結果にも approval: false が付きます）。",
        "取込の bytes は読みません（読むのは Host）。source_digest・preview_digest は形と対象だけを検め、計り直しません。変換の正しさ・issues の位置の正しさ・件数の正しさは確かめません。",
        "大きさ・先頭の bytes と拡張子・UTF-8・JSON の同じキーの検出は Host の役目です。継手は receipt の形と理由の組み合わせだけを検めます。",
        "書いた bytes の検めは、Host が渡した bytes が実のファイルと同じであることを前提にします。cells_escaped は上限だけを確かめます。CSV の見出しや JSON のキーが mapping の名前か、値の型は確かめません。",
        "式への対策は契約の trigger（= + - @ TAB CR）だけです。先頭の空白の後の式・全角の記号・表計算ソフトごとの別の解釈（DDE など）は範囲の外です。",
        "handle の状態と結果の分からない書き出しは、この継手の中だけにあり、再起動で消えます。outputChecked は App の申告を受け取るだけで、App が本当に確かめたかは確かめません。違う版を名乗る port が本当にその版を持つかも確かめません。",
        "特定の表計算ソフト・ファイル選択の仕組みの使い方の雛形ではありません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/file-io/v1（FileIoRequest / FileIoReceipt）でつなぎます。createFileIoClient({ cfg, port }) の admitHandle で App が handle を渡し、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: Host への port（submit、任意で lookup、書き出しには readOutput）と cfg の pin（版・op・形式・上限・mapping）。副作用: 下見は NONE、書き出しは LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-15",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "AI 実行の接続 — AI 実行接続の継手（Tsugite）",
      "summary": "AI 実行接続の継手（Tsugite）は、Runtime が固定した 1 回の AI の呼び出し（operation・slot・許可された context・予算の予約・試行の番号）を、要求（AiExecutionConnectRequest）として AI サービスへ運び、返った receipt（AiExecutionConnectReceipt）を照合する継手（つぎて）です。届いた状態・使用量・精算を分けて照合し、分からないものを 0 にせず、自動では送り直しません（fail-closed）。",
      "problem": "AI の呼び出しが時間切れや通信の喪失で終わったとき、届いたか分からないのに使用量を 0 として予約を放すことや、そのまま送り直して二重に呼び出すことを防ぎます。呼び出しの裏で見えにくい、使用量と結果の記録の食い違いを、照合で表に出します。",
      "use_when": [
        "1 回の AI の呼び出しを、配備が許す provider と model の組、許された context の参照だけ（32 件まで）で、AI サービスへ運びたいとき。",
        "時間切れ・通信の喪失を、届いたか分からない（UNKNOWN_DELIVERY）・使用量 UNKNOWN・予約は持ったまま・再試行は止める、として受け取りたいとき。",
        "送り直しを、前の試行が FAILED_CONFIRMED か NOT_EXECUTED_PROVEN と分かった後の新しい試行としてだけ出したいとき。新しい試行は、新しい request_id・operation_id・予約で、同じ予算から、1 枠 3 回までです。",
        "AI サービスの capability の宣言の digest と通信の上限（transport_limit_ms）を要求に結び、宣言と合わない呼び出しを送る前に止めたいとき。",
        "COMPLETED の reply を、bytes の大きさと sha256 を確かめてから受け取りたいとき。"
      ],
      "do_not_use_when": [
        "provider を直接呼びたいとき。provider の呼び出しは AI サービスの役目で、この継手は Runtime 側の半分です。",
        "App や部品（Kernel）から AI を呼びたいとき。この継手を呼べるのは Runtime だけです。",
        "失敗したら自動で送り直してほしいとき。この継手は自動では送り直さず、新しい試行は Runtime が決めます。",
        "予算・計画・照合の判断を任せたいとき。それは Runtime の役目です。照合の結果（verdict）は Runtime が決め、この継手は記録するだけです。",
        "すぐに使える AI サービスへの接続がほしいとき。実の AI サービス・provider には一度も接続しておらず（呼び出しは 0 回）、port の実装・設定の値は接続するときに別の関門で決めます。",
        "特定の AI provider の SDK や課金 API の使い方の雛形がほしいとき。この継手はその雛形ではありません。"
      ],
      "what_it_checks": [
        "呼ぶ側と設定を検めます。Runtime でない呼び出し、配備が許さない provider と model、設定に無い通貨、配備の固定と違う契約の版の要求は送りません。空の表は何も許しません。",
        "要求の形を検めます。生の鍵の欄、33 件以上や重複した context の参照、小数のお金、予約の tokens を超える max_output_tokens、無い・範囲（1,000〜600,000 の整数）の外の transport_limit_ms、無い・別の領域・別の provider を指す宣言の digest、要求の時刻より後の ai_service_capability_read_at を持つ要求は送りません。",
        "provider 側の文書（ai-execution-request/v1）を検めます。sha256(canonical) が provider_request_digest と違う、provider・model が要求と違う、許されていない参照・生の本文・retry_profile・鍵の実体に見える値・秘密の欄の名前・参照の形でない auth_ref を含む文書は送りません。",
        "試行の順を検めます。1 枠の 4 回目、前の試行の無い 2 回目、前の試行の結果に合わない prior_verdict、照合の前や RESPONSE_DURABLE の後の再試行、別の予算・使った予約・使った operation_id・別の slot・前の receipt と合わない digest の試行は送りません。",
        "宣言の文書（declareCapabilities() の文書）を添えたときは、sha256(canonical) が要求の digest と、transport_limit_ms の欄が要求の上限と同じかを照らします。小数・安全でない整数・穴のある配列・getter などで canonical の形に収まらない宣言は、照らさずに断ります。",
        "receipt を照合します。request_digest の偽り、別の operation・予約、時間切れなのに使用量を 0 にする・予約を放す・再試行を許す・届いたとする receipt、provider の受け取りや reply の無い COMPLETED、根拠の無い SETTLED、プロンプトや鍵をログに出したとする receipt は受理せず、停止します（HALTED）。",
        "受け取りの版と出どころを照合します。pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION、AI サービスが Runtime の記録を名乗る receipt、同じ provider の受け取りを別の operation で使う receipt（RECEIPT_CONFLICT）は受理しません。CAPABILITY_MISMATCH の後の試行は、その受け取りの current_capability_digest と同じ digest のときだけ送ります。",
        "COMPLETED は readReply で reply の bytes を読み、長さが byte_size（16,384 まで）で sha256 が reply_digest と同じときだけ受け取ります。読めなければ UNKNOWN、合わなければ受理せず停止します。"
      ],
      "what_it_does_not_do": [
        "届いたことは、中身が正しいことでも採用でもありません。どの結果にも approval: false と automatic_re_request: false が付き、再依頼は Runtime が新しい試行として決めます。",
        "reply の中身の意味・正しさ・採用は確かめません（届いたことと bytes の sha256 だけ）。照合の結果（verdict）の正しさも確かめません。",
        "使用量の値・単価・精算の正しさ、予約の額が足りるかは確かめません。予約を超えた使用量も、そのまま事実として返します。",
        "鍵は扱いません。鍵は AI サービスが持ち、参照で指します（HELD_BY_PORT）。port の例外の message も、鍵の実体を含みうるので結果に写しません。",
        "試行の記録・予約 ID・operation_id・見た provider の受け取りは、この継手の中だけにあり、再起動で消えます。再起動の後は、2 回目からの試行を送りません。",
        "答えの無い試行をいつ諦めて Runtime の記録（recordNoAnswer）にするかは決めません。この継手は時計を持たず、記録の時刻が要求の時刻＋transport_limit_ms 以上であることだけを確かめます。",
        "添えた宣言の文書や current_capability_digest が本当に AI サービスの今の宣言のものか、Runtime が本当に宣言を読み直したかは確かめません。実の AI サービスが CAPABILITY_MISMATCH で断るかは、代役で試しただけです。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/ai-execution-connect/v1 でつなぎます。createAiExecutionConnectClient({ cfg, port }) の submit(request, providerRequest, declaration?) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。Runtime は recordNoAnswer・recordReconciliation で記録します。利用側の用意: AI サービスへの port（submit・readReply、任意で lookup）と cfg の pin。副作用: 依頼は EXTERNAL_IRREVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-16",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "表示・再生の接続 — 表示・再生接続の継手（Tsugite）",
      "summary": "表示・再生接続の継手（Tsugite）は、部品（Kernel）が作った view model を、digest で固定した素材と再生設定とともに App の画面へ渡し（ViewPlaybackRequest）、App が返した receipt（ViewPlaybackReceipt）を照合する継手（つぎて）です。描くのは App で、この継手は描きません。見せた・再生したは、App の build と出力の digest で確かめます（fail-closed）。",
      "problem": "「表示した」「再生した」を裏付けなしに信じてしまうこと、同じ利用者の操作を二度命令にしてしまうこと、そして見せたことを権限の変更と取り違えることを防ぎます。",
      "use_when": [
        "view model・素材・再生設定を digest で固定して App の画面へ渡し、見せた結果を App の build と出力の digest で確かめたいとき。",
        "同じ入力（op・view model・view 契約・素材・再生設定・App の build）の出力の digest が同じであること（再現）を確かめたいとき。",
        "利用者の操作を、view（view_session_id）ごと・operation_id ごとに 1 回だけ、typed command として受け取りたいとき。",
        "欠けた素材を、理由（置き場に無い NOT_FOUND／digest が違う DIGEST_MISMATCH）と観測した digest つきで受け取りたいとき。"
      ],
      "do_not_use_when": [
        "画面に描く処理そのものがほしいとき。描くのは App で、この継手は描きません。",
        "画像を作りたいとき。画像の生成は、この継手の範囲の外です。",
        "typed command を適用したいとき。この継手は命令を送りません。適用は、内容・版・差分適用の継手か Runtime の役目です。",
        "スクリプトなど、image・audio・video・text・font 以外の media type を素材にしたいとき。",
        "すぐに使える画面・素材の置き場がほしいとき。実の App・画面・素材の置き場には一度も接続しておらず（描画・再生は 0 回）、port の実装・設定の値は接続するときに別の関門で決めます。",
        "特定の UI フレームワーク・プレイヤー・描画エンジンの雛形がほしいとき。この継手はその雛形ではありません。"
      ],
      "what_it_checks": [
        "設定と依頼を検めます。配備の固定と違う契約の版、設定（view_contracts・media_types）に無い view 契約・版・media type、スクリプトの media type、257 個以上の素材、同じ asset_ref を二度書く依頼、side effect を宣言した依頼は送りません。空の表は何も許しません。",
        "見せた結果を照合します。RENDER に PLAY で答える（またはその逆の）receipt、見せた証跡の無い receipt、依頼と違う view model・設定の digest、同じ入力なのに出力の digest が変わった receipt は受理しません。",
        "欠けた素材を照合します。一覧の無い ASSET_MISSING、依頼に無い素材や media type、理由の無い欠け、観測 digest の無い・宣言と同じ値の DIGEST_MISMATCH、digest つきの NOT_FOUND、同じ素材の欠けを 2 つ挙げる receipt は受理しません。",
        "操作を照合します。重複した operation_id の挙げ忘れ、新しい操作を重複と言う receipt、見せなかった view に操作がある receipt、RENDER に再生の操作がある receipt、1,001 個以上や知らない種類の event は受理しません。命令にするのは、新しい operation_id の event だけです。",
        "receipt の宣言を照合します。画像を作った、Runtime に UI を足した、権限を与えた、AI を呼んだとする receipt は受理しません。",
        "版と相関を照合します。request_digest の偽り、pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION、別の view（view_session_id）の receipt、対象の違う digest は受理せず、停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "見せたことは何も許しません。どの結果にも approval: false と permission_change: NONE が付きます。",
        "画面に実際に何が見えたか（画素・音）は確かめません。出力の digest は App の決定的な描画の記述の digest で、正しさは App の責任です。",
        "素材の bytes は持ちません。bytes の検め（NOT_FOUND・DIGEST_MISMATCH）は App 側の役目で、App が全部の欠けを挙げたか、観測 digest が本当に観測した値かは確かめられません（checkAssetBytes を App 側の検めとして出すだけ）。",
        "view model そのものの正しさ、view 契約の中身、typed command の中身は確かめません（digest と ref だけ）。",
        "view の記録（見た operation_id・再現の鍵）は、この継手の中だけにあり、再起動で消えます。再起動の後の重複は数えられません。",
        "App から押し出された receipt は受け取りません。submit の答えの receipt だけを照合し、同じ request_id の再送には App が同じ receipt で答えることを前提にします。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/view-playback/v1 でつなぎます。createViewPlaybackClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false・permission_change: NONE）。DONE には output_digest・missing_assets・commands（typed command）などが付きます。利用側の用意: App の画面への port（submit、任意で lookup）と cfg（版・view 契約・media type）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-17",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "複数参加者の共有の版 — 複数参加者・共有版の継手（Tsugite）",
      "summary": "複数参加者・共有版の継手（Tsugite）は、参加者の event（変更の提案 PROPOSE_CHANGE・同意 CONSENT・同意の撤回 WITHDRAW_CONSENT・脱退 LEAVE・同期 SYNC）を共有 Host へ 1 つずつ運び（SharedRevisionRequest）、receipt（SharedRevisionReceipt）を照合する継手（つぎて）です。同意を（誰・どの版・その版の digest）に結び、次の版へ持ち越させません（fail-closed）。",
      "problem": "共同編集で、古い版への同意を新しい版への同意として数えてしまうこと、AI の役を人間の参加者として数えてしまうこと、切断で結果の分からない event を出し直して二重に適用させてしまうことを防ぎます。",
      "use_when": [
        "複数の参加者が共有の場で変更を提案し、同意・撤回・脱退する流れを、event 1 つずつ契約どおりに運びたいとき。",
        "同意を（誰・版・digest）に結び、変更が通ったら古い版への同意が失効として挙がることを確かめたいとき。",
        "AI の役を、認証を記録した人間が操作するものとしてだけ参加させ、同意・撤回・脱退をさせず、人間として数えたくないとき。",
        "切断で結果の分からない event を、同じ event_id のまま送り直して解きたいとき。"
      ],
      "do_not_use_when": [
        "今すぐ共有の場を動かしたいとき。共有 Host の所管は未定で、Host そのものがありません。決まるまでは、どの event も HOST_NOT_ASSIGNED で断られ、何も適用されません。",
        "共有 Host・共同編集サービス・同意管理サービスそのものがほしいとき。この継手は Host を作りません。",
        "共有の内容の差分を適用したいとき。それは内容・版・差分適用の継手（P-13）の役目です。event を並べて適用し、版・同意・重複除きを持つのは共有 Host の役目です。",
        "参加者の本人確認をさせたいとき。この継手は認証の証跡の ref を運ぶだけで、本人確認は Host の役目です。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定と参照を検めます。設定が無い、契約の pin が contract_schema_digest と違う、設定と違う host_ref、許していない作業場（workspace_ids）、..・ドライブ・制御文字・U+2028 を含む参照、知らない op・余分な欄・op に合わない本文や意図の要求は送りません。",
        "参加者を検めます。認証の証跡を記録していない人間、登録していない AI の役、操作する人の違う・自分を操作する AI の役、同じ参照を人間と AI の役に混ぜた要求、脱退した参加者（とその人が操作する AI の役）の event は送りません。",
        "AI の役の同意・撤回・脱退は送りません。AI の同意を受け付けたとする receipt、AI の役を名指す失効・脱退の通知は受理しません。",
        "版と同意を照合します。新しい版が基にした版 + 1 でない変更、版を進める変更でない event、知っている版より前に戻る版、基にした版での CONFLICT、依頼と違う（誰・版・digest）の同意の記録、同意の持ち越し、通知の無い失効は受理しません。同意は基にした版そのものへだけ送ります。",
        "撤回・脱退の答えを照合します。終わった同意を挙げない撤回、ほかの人や今でない版への同意を挙げる答え、知っている本人の同意を欠く脱退、invalidated_consents の欄の無い脱退、撤回でない op の CONSENT_NOT_FOUND は受理しません。",
        "送り直しを検めます。同じ event_id や同じ request_id で別の中身の要求、結果の分からない event があるのに新しい event_id で出し直す要求は送りません。新しい event への DUPLICATE_EVENT、再送への EVENT_ID_REUSE は受理しません。",
        "receipt を照合します。request_digest の偽り、別の op や event、op の表の外の状態、効果を増やす receipt、pin の無い・違う版の receipt、共有フォルダーの DB・暗号化 blob を共有 Host と名乗る receipt は受理せず、停止します（HALTED）。"
      ],
      "what_it_does_not_do": [
        "受け付けられた・同意が記録されたことは、内容が正しいことでも、ほかの参加者の同意でもありません（どの結果にも approval: false が付きます）。",
        "共有の内容（change_ref の中身）と状態の digest（target_digest）が正しいかは確かめません。",
        "認証の証跡は ref を運ぶだけです。本人確認そのものと、AI の役を操作する人が本当にその人かは確かめません。",
        "参加者・同意・版の記録は、この継手の中だけにあり、再起動で消えます。ほかの端末で行った同意・撤回・脱退は、通知か Host の答えでしか知りません。Host がほかの参加者全員に通知を届けたかは確かめません。",
        "実の共有 Host には一度も接続していません。試験は手元の代役と HOST_NOT_ASSIGNED の port だけで、確かめの順（pin → Host の割り当て → 参加者と event）も代役でしか確かめていません。",
        "observed_timestamp は形だけを検め、時計の正しさは確かめません。答えた port が本当にその版を持つかも確かめません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/shared-revision/v1 でつなぎます。createSharedRevisionClient({ cfg, port }) の recordAuthentication・registerAiRole で人間と AI の役を記録し、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: 共有 Host への port（所管が未定の間は createHostNotAssignedPort）と cfg（版・host_ref・workspace_ids）。副作用: SYNC は NONE、ほかは EXTERNAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-18",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "限定された操作・観測の接続 — 限定操作・観測接続の継手（Tsugite）",
      "summary": "限定操作・観測接続の継手（Tsugite）は、試行の 1 歩（観測 OBSERVE・許された 1 つの操作 ACT・初めの snapshot への戻し RESET）を App の sandbox の操作 Host へ運び（SandboxOperationRequest）、receipt（SandboxOperationReceipt）を照合する継手（つぎて）です。範囲は sandbox の中だけで、外への送信・購入・広い desktop の権限を名乗る要求も receipt も通しません（fail-closed）。",
      "problem": "試しの操作が sandbox の外に漏れること、結果の分からない操作を再クリックで埋めて二重に操作すること、そして戻せなかった環境のまま次の試行を進めてしまうことを防ぎます。",
      "use_when": [
        "試しの環境（sandbox）での観測・許された操作・初めの状態への戻しを、1 歩ずつ契約どおりに運びたいとき。",
        "操作を、pin した操作の集合の中で、その試行の最新の観測（まだ操作の基にしていないもの）に基づくときだけ送りたいとき。",
        "試行を、読み戻した snapshot が初めと同じ reset（RESET_VERIFIED）で始め、sequence を 0 から 1 ずつ進めたいとき。",
        "reset に失敗したら、人が戻したと記録する（recordHumanRestore）まで、その sandbox へ何も送らないようにしたいとき。"
      ],
      "do_not_use_when": [
        "実サイトへの送信・購入・広い desktop の操作をしたいとき。この継手の範囲は sandbox の中だけです。",
        "観測・操作・reset そのもの、時間の計測、sandbox の隔離を任せたいとき。それは App の sandbox と操作 Host の役目で、この継手は操作しません。",
        "到達の判定（goal の検査）を任せたいとき。goal の検査は操作 Host の役目です。",
        "画面操作の自動化ツール・ブラウザー操作の雛形がほしいとき。この継手はその雛形ではありません。",
        "すぐに使える sandbox がほしいとき。実の App の sandbox・操作 Host には一度も接続しておらず、試験は手元の代役（3 ページの練習用アプリ）だけです。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "設定と範囲を検めます。設定が無い、契約の pin が contract_schema_digest と違う、許していない sandbox、pin していない・digest の違う操作の集合、集合の外の対象・操作の種類・selector、sandbox の外への送信・購入・広い desktop を名乗る要求、範囲の外の上限（max_actions 1〜200・max_seconds 1〜3600）は送りません。",
        "要求の形を検めます。..・ドライブ・制御文字を含む参照、知らない op・余分な欄・op に合わない意図、知らない操作の種類、入力の無い TYPE_TEXT、対象の違う digest の要求は送りません。",
        "試行の順を検めます。RESET で始めない試行、使った trial_id、止まった sandbox・終わった試行、sequence の飛び・繰り返し、途中で条件を変えた試行は送りません。max_actions に達したら ACT を送りません。",
        "操作を検めます。観測の無い・古い観測に基づく ACT、1 つの観測での 2 つ目の ACT、同じ action_id の再送（再クリック）、ACTION_UNKNOWN の後の ACT・RESET は送りません。ACTION_UNKNOWN の後は OBSERVE だけを送ります。",
        "reset を照合します。初めと違う snapshot の RESET_VERIFIED、次の試行を許す RESET_FAILED、初めと同じ snapshot の RESET_FAILED は受理しません。",
        "receipt を照合します。request_digest の偽り、別の op・試行・sequence、外へ送信した・購入したとする receipt、効果を増やす receipt、goal の無い到達の結果、操作の記録の digest の無い ACTION_PERFORMED、数え違いや上限を超える actions_used、op の表に無い断りの理由、pin の無い・違う版の receipt は受理せず、停止します（HALTED）。",
        "port の障害で結果の分からない要求は送り直しません。lookup で記録を読むだけで、見つからなければ OBSERVE・ACT はその試行を終え、RESET は失敗と同じに扱って、人が戻すまで sandbox を止めます。"
      ],
      "what_it_does_not_do": [
        "操作した・到達したことは、操作が正しいことでも、利用者が採用したことでもありません（どの結果にも approval: false が付きます）。",
        "sandbox の隔離・購入の防止・画面の権限は確かめません。receipt の外への送信 0・購入 0 を照合するだけで、実際に防がれたかは操作 Host の役目です。",
        "観測・snapshot の中身と digest が画面と合っているか、goal の検査が正しいか、操作の記録が本当にその操作のものかは確かめません。",
        "時計を持たず、max_seconds の計時はしません。時間切れは操作 Host の LIMIT_REACHED で知ります。操作の集合の文書そのものも読まず、設定の組と digest の値で照合するだけです。",
        "試行の記録は、この継手の中だけにあり、再起動で消えます。人が sandbox を戻したことは証跡の ref で知るだけで、戻ったかは次の reset の読み戻しで確かめます。",
        "SEQUENCE_GAP・OBSERVE の TARGET_NOT_PERMITTED の断りは Host の言い分をそのまま受け取り、食い違いの原因は確かめません。答えた port が本当にその版を持つかも確かめません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/sandbox-operation/v1 でつなぎます。createSandboxOperationClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。止まった sandbox は recordHumanRestore で人の戻しを記録します。利用側の用意: 操作 Host への port（submit、任意で lookup）と cfg（版・sandbox_refs・action_sets）。副作用: OBSERVE は NONE、ACT・RESET は LOCAL_REVERSIBLE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-19",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "資料・素材の参照 — 資料・素材参照の継手（Tsugite）",
      "summary": "資料・素材参照の継手（Tsugite）は、利用者が指定したローカルの資料の 1 つの場所を、版（exact_revision）か file digest で固定し、使用条件と役の範囲を付けて資料の Host に参照を頼み（MaterialReferenceRequest）、返った receipt（MaterialReferenceReceipt）を契約で照合する継手（つぎて）です。固定して参照するか、何も返さないかのどちらかです（fail-closed）。",
      "problem": "参照している資料が途中で差し替わり、気づかないまま新しい版を使ってしまうことを防ぎます。資料の中の命令らしい文を実行の命令として扱うことと、分からない出典を推測で埋めることも防ぎます。",
      "use_when": [
        "利用者が指定したローカルの資料を、版か digest で固定し、使用条件つきで参照したいとき。",
        "原本が変わっていたら、新しい版へ黙って移らず、REVISION_CHANGED と観測した版・digest で知りたいとき。",
        "資料の決めた場所（頁・行・文字・時間の範囲など）だけを、sha256 と大きさを確かめた bytes のまま受け取りたいとき。",
        "許可の取り消し（PERMISSION_REVOKED）・参照切れ（NOT_FOUND）・位置が無い（LOCATOR_UNRESOLVED）を、それぞれの状態として受け取りたいとき。"
      ],
      "do_not_use_when": [
        "URL や別のサービスなど、外の資料を参照したいとき。外の資料は範囲の外で、能力の宣言と許可が別に要ります。",
        "資料の中身の正しさや、事実かどうかを確かめたいとき。この継手は中身を解釈せず、確かめません（NOT_VERIFIED）。",
        "原本が変わったとき、新しい版で自動で参照し直してほしいとき。新しい固定での依頼は作らず、利用者か Runtime が決めます。",
        "特定の文書管理・検索サービスの使い方の雛形がほしいとき。この継手はその雛形ではありません。",
        "すぐに使える資料の Host がほしいとき。実の資料・資料の Host には一度も接続しておらず（実の参照は 0 回）、port の実装・資料の指定の仕組み・設定の値は接続するときに別の関門で決めます。",
        "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
      ],
      "what_it_checks": [
        "資料と版を検めます。設定の allowed_source_handles に無い資料、URL・Windows のパスの handle、LOCAL_DESIGNATED でない source_locality、contract_schema_digest と違う契約の pin の依頼は送りません。空の一覧は何も許しません。",
        "固定・位置・使用条件を検めます。固定が無い・2 つある・別の資料の digest・空の版、終わりの無い範囲・始まりが終わりより後・頁や行が 0 から、範囲つきの全体や anchor、知らない位置の種類、使用条件の無い依頼は送りません。",
        "固定の照合をします。固定と合わないのに RESOLVED とする receipt、新しい版を黙って返す receipt、中身を返す・観測の無い REVISION_CHANGED、観測と矛盾する pinned_matches、pin が合わない LOCATOR_UNRESOLVED、pin を返さない receipt は受理しません。",
        "資料はデータとして扱います。実行できるとする receipt、埋め込みの命令を許す receipt（embedded_instructions_authorized が false でない）、資料を確かめた事実とする receipt は受理しません。",
        "出典を照合します。source_identity の無い KNOWN、推測した出典の状態、名前を付けた UNKNOWN は受理しません。",
        "receipt を照合します。request_digest の偽り、使用条件や位置を返さない receipt、効果を行ったとする receipt、余分な欄、pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION は受理せず、停止します（HALTED）。",
        "中身を照合します。readContent は RESOLVED の参照だけを読み、byte_size が 1 MiB を超える receipt は受理せず停止します。設定の max_content_bytes を超えるものは読まずに断ります（REFUSED）。読んだ bytes は、長さと sha256 が receipt と同じときだけ bytes のまま返し、合わなければ停止します。"
      ],
      "what_it_does_not_do": [
        "参照できたことは、確かめた事実でも、使ってよいという判断でもありません（どの結果にも approval: false が付きます）。",
        "中身の bytes を解釈しないので、資料の中の文の意味（命令らしい文かどうか）は判定しません。受け取った側がデータとして扱うことは、その側の役目です。",
        "固定と原本の比較、位置の切り出し、許可・役の範囲・使用条件の判断は、資料の Host と Runtime の役目です。この継手は receipt の形と組み合わせだけを検め、観測を固定と同じ値で偽る Host は見分けられません。",
        "その場所が本当に固定した版の中にあるか、切り出しが単位どおりか（頁の区切り方・code point の数え方・時間の位置）は確かめません。byte_size と読んだ bytes の長さ・sha256 だけを照らします。",
        "KNOWN の source_identity が本当に記録されたものか、handle の資料が本当に利用者が指定したローカルの資料かは確かめません。",
        "記録はこの継手の中だけにあり、再起動で消えます。前の参照の失効の報告は Runtime の役目です。答えた port が本当にその版を持つかも確かめません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/material-reference/v1 でつなぎます。createMaterialReferenceClient({ cfg, port }) の submit(request) で参照を頼み、readContent(request_id) で RESOLVED の中身を bytes のまま受け取ります。結果は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED です（どれも approval: false）。利用側の用意: 資料の Host への port（submit、任意で lookup、readContent には readScoped）と cfg（版・資料の一覧・中身の上限）。副作用: NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-20",
      "category": "P/アプリ基盤",
      "kind": "Tsugite",
      "title": "書き出し・取り込みの往復 — Studio 往復・移行の継手（Tsugite）",
      "summary": "Studio 往復・移行の継手（Tsugite）は、候補の書き出し（EXPORT_RETURN）・書き出し先の読み取り（INSPECT_EXPORT）・受領書の検証（VERIFY_STUDIO_RECEIPT）・データ移行の preview（MIGRATION_PREVIEW）・移行の決定（MIGRATION_DECIDE）・移行の状態の読み取り（MIGRATION_STATUS）の要求（StudioRoundtripRequest）を Runtime へ運び、receipt（StudioRoundtripReceipt）を照合する継手（つぎて）です。上書きも自動のやり直しもしません（fail-closed）。",
      "problem": "作業の結果を元の場所へ戻す往復で、書き出し先にあるファイルを上書きすること、検証していない受領書や利用者が確かめていない diff で移行を決めること、結果の分からない書き出し・決定を送り直して二重に書く・決めることを防ぎます。",
      "use_when": [
        "候補を新しいファイルとして書き出し（create-new）、効果の分からない書き出しを、書き出し先の読み取りで解きたいとき。",
        "データ移行の preview を、検証済みで受け入れられた（VERIFIED・ACCEPTED）同じ base package の受領書のときだけ送りたいとき。",
        "移行の決定を、照合した preview の diff と同じ digest と、利用者本人の確認（recordConfirmation）があるときだけ送りたいとき。",
        "採用（COMMITTED）を、新しい世代（古い世代 + 1）・新しい session で、古い世代を残すときだけ受け取りたいとき。",
        "結果の分からない決定を、移行の状態（MIGRATION_STATUS）を読み、どの決定が効いたかの表で解きたいとき。"
      ],
      "do_not_use_when": [
        "受領書の署名の検証、diff の計算、移行そのものを任せたいとき。それは Runtime の役目で、この継手は送る前と受け取った後の検めだけをします。",
        "書き出し先のファイルを置き換えたいとき。書き出しは新しいファイルだけで、書き出し先に何かがあれば Runtime は何も書かずに DESTINATION_EXISTS で答えます。",
        "受領書の検証や移行の結果を、package の実行の許可や採用の承認として使いたいとき。",
        "すぐに動く往復がほしいとき。相手の Runtime 側の口（取り込み・移行・書き出し先の読み取りなど）はまだ設計だけか無く、port は試験の代役だけです。実の書き出し・読み取り・検証・移行は 0 回です。",
        "業務の規則を組みたいとき。この継手はアプリ基盤の継手で、業務の規則は部品（Kernel）と構成で担います。"
      ],
      "what_it_checks": [
        "設定と要求の形を検めます。設定が無い、契約の pin が contract_schema_digest と違う、destination_handle_refs に無い書き出し先（読み取りも）、..・ドライブ・制御文字・U+2028 を含む参照、知らない op・余分な欄・ほかの op の欄、自動で適用する書き出し（automatic_apply）、空や繰り返しの候補の要求は送りません。",
        "preview と決定を検めます。検証済みで受け入れられた受領書の無い・別の base package・検証し直して通らなかった preview、知らない移行・別の diff の digest・確認の無い決定、HUMAN でない・別の決定や移行や diff に結んだ・使った確認、同じ preview の 2 回目の決定は送りません。",
        "受領書の検証の答えを照合します。UNVERIFIED・MISMATCH・受け入れられていない受領書を preview に進めるとする答え、発行 package の digest の無い ACCEPTED、studio_receipt_id の無い VERIFIED は受理しません。",
        "決定と移行の答えを照合します。ADOPT に DECLINED・DECLINE に COMMITTED で答える receipt、decision を返さない・入れ替える receipt、古い世代を落とす・同じ世代・世代の飛ぶ COMMITTED、別の移行の receipt は受理しません。",
        "書き出し先の答えを照合します。found_bundle の無い・形の違う・自動で適用する EXPORT_PRESENT、found_bundle の付いたほかの状態、digest の無い・対象の違う EXPORT_UNREADABLE、EXPORT_RETURN でない op の DESTINATION_EXISTS、効果を名乗る読み取りや断りは受理しません。",
        "書き出し・決定の結果不明は送り直しません。書き出しは記録（lookup）か書き出し先の読み取りで、決定は記録か移行の状態で解き、解けるまでほかは送りません。書き出し直すのは EXPORT_ABSENT のときだけで、別の bundle や読めないファイルがある書き出し先へは何も書きません。",
        "receipt を照合します。request_digest の偽り、別の op の receipt、op の表の外の状態、原本を編集・発行・再署名したとする receipt、受領書だけで実行を許すとする receipt、効果の増えた receipt、op に合わない断りの理由、pin の無い・違う版の receipt は受理せず、停止します（HALTED）。",
        "送った決定と違う決定が効いたと移行の状態が示したら、異常とせず事実として other_decision_took_effect: true を添えて返します。その移行はもう決めません。"
      ],
      "what_it_does_not_do": [
        "受領書を検証できた・preview に進める・移行できたことは、package の実行の許可でも採用の承認でもありません（どの結果にも approval: false が付きます）。",
        "受領書の署名の検証・信頼する鍵・bundle や diff の bytes の sha256・移行そのものは確かめません。receipt の値を照合するだけです。",
        "found_bundle が本当に書き出し先のファイルから読んだ中身か、読み取りが何も書かなかったか、create-new が本当に置き換えないかは確かめません。create-new に背く Runtime から守れるのは、読んで別の bundle・読めないファイルがあると知った書き出し先だけで、それも再起動までです。",
        "人の判断が要る答え（needs_human_decision: true）の後に、人が何をしたかは知りません。止めて人に尋ねるのは呼ぶ側です。",
        "照合した受領書・preview・利用者の確認・書き出し先の記録は、この継手の中だけにあり、再起動で消えます。",
        "COMMITTED などの状態が、この継手の送った決定の効果か、別の送り手の同じ決定の効果かは見分けられません。",
        "書き出し・決定の結果不明が解けないとき（記録が無い、状態が CREATED・PREVIEWING のまま、読み取りが断られる）は、UNKNOWN のまま止まり、人の確認を待ちます。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
      ],
      "integration": "契約 erabazu.candidate/certified-tsugite/studio-roundtrip/v1 でつなぎます。createStudioRoundtripClient({ cfg, port }) の recordConfirmation で利用者の確認を受け取り、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: Runtime への port（submit、任意で lookup）と cfg（版・destination_handle_refs）。副作用: 書き出し・preview・採用は LOCAL_REVERSIBLE、読み取り・検証・辞退・状態は NONE。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-21",
      "category": "P/アプリ基盤",
      "kind": "Component",
      "title": "型つきの答えの取り込み — 型つきの答えの取り込みの部品（Component）",
      "summary": "型つきの答えの取り込みの部品（Component）は、AI の答えの文字列を、決まった封筒（status・binding・枝ごとの中身）と使う側が渡す判断の schema で検め、採用の候補の下書き（PROPOSAL_CANDIDATE）・HOLD・OUT_OF_SCOPE に分ける部品です。答えの読み取りと型の検めは部品(Kernel) P-01 に port で頼み、枝の決まりと binding の 8 つのキーの照合はこの部品が行います。PROPOSE の答えでも採用しません（NOT_ADOPTED）。",
      "problem": "AI の「提案」の裏で、その答えが古い入力に対するものでないかは見えません。この部品は、型の違う答え・古い入力の答え・読めない port の答えを候補にせず（fail-closed）、binding の 8 つのキーが期待と違う答えを STALE_OR_UNBOUND で止めます。モデルが答えの中で「valid」などと申告しても、それを根拠にしません。",
      "use_when": [
        "AI の答えの文字列（answer_text、4,194,304 の符号単位まで）を、封筒 { status, binding, proposal | questions | reason } と使う側の判断の schema（proposal_schema）で検め、PROPOSAL_CANDIDATE・HOLD・OUT_OF_SCOPE のどれかとして受け取りたいとき（intake）。",
        "答えが、呼び出し側の期待する条件（product_id・case_id・role・input_revision・input_digest・selection_digest・profile_revision・request_id の 8 つ）の上のものかを、UTF-16 の符号単位の完全一致で照らしたいとき。違えば STALE_OR_UNBOUND で、違うキーを決まった順で detail.mismatched に返します。",
        "status ごとの中身の決まり（PROPOSE は proposal だけ、HOLD は questions だけ、OUT_OF_SCOPE は reason だけ）を、P-01 が合うと答えた後に確かめたいとき。違えば ANSWER_NOT_TYPED（stage: branch）です。",
        "型の違いを、どの段で外れたか（detail.stage: json・envelope・proposal・branch）と、P-01 の issue（位置とコード、20 件まで）で受け取りたいとき。",
        "どの判断の schema で検めたかを、封筒と判断の schema を 1 つにした schema の digest（schema_digest）で結果に添えたいとき。",
        "例外を投げず、入力を書き換えず、時計・乱数・状態を持たない処理として、同じ答えと同じ P-01 の答えから同じ結果を受け取りたいとき。"
      ],
      "do_not_use_when": [
        "PROPOSAL_CANDIDATE を、採用・保存・外への適用として使いたいとき。PROPOSAL_CANDIDATE は「型と binding が合う下書き」で、内容が正しいこと・採用・保存の許可ではありません。採用の記録は使う側の役目です。",
        "proposal の内容が正しいか、答えの根拠（basis）が確かかを確かめたいとき。この部品が見るのは型と binding だけです。根拠の検めは使う側の役目です。",
        "安全な整数を超える数を、数として正確に受け取りたいとき。proposal は JSON.parse で読んだ値で返すため、そうした数は丸まりえます。",
        "Promise を返す非同期の port を P-01 の代わりにつなぎたいとき。非同期の port には対応せず、PORT_FAILED で止めます。",
        "既にある判断の schema の候補の形へ、この封筒の答えを移したいとき。移し替えはこの部品の外の別の adapter の役目で、既存の schema は変えません。",
        "本人の確認や、答えを残すことまで任せたいとき。この部品は答えを残さず、本人の確認もしません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "options が素のオブジェクトでない・知らない名前や getter があるとき INVALID_OPTIONS、contract_check か schema_digest が関数でないとき PORT_MISSING、schema_id が 1〜256 文字の文字列でないとき INVALID_SCHEMA_ID、proposal_schema が素の JSON のオブジェクトでない（クラスのインスタンス・getter・穴・有限でない数・入れ子 64 段を超える）とき INVALID_PROPOSAL_SCHEMA で止めます。",
        "封筒と判断の schema を 1 つにした schema を P-01 が読めない、または信頼しない（部分集合の外のキーワードなど）とき、PROPOSAL_SCHEMA_NOT_TRUSTED で止めます。",
        "P-01 が投げた、答えの形が読めない、state が done でない、重複するキーを見ていない、合うと言った答えが JSON・封筒として読めない（偽りの答え）とき、通さずに PORT_FAILED で止めます。",
        "answer_text が文字列でない（箱の文字列を含む）・4,194,304 の符号単位を超えるとき INVALID_ANSWER、expected_binding が 8 つのキーをちょうど 1〜256 文字の文字列で持つ素のオブジェクトでないとき INVALID_EXPECTED_BINDING（detail.path）で止めます。",
        "自然文・壊れた JSON・単独のサロゲート・UTF-8 で 1 MiB を超える答え・重複するキー・封筒の外のキー・知らない status・判断の schema の外の proposal・枝の中身の欠けや混ざりは、ANSWER_NOT_TYPED で止めます。",
        "型の検めが通った後、binding のキーが 1 つでも期待と違えば（大文字小文字の違いを含む）STALE_OR_UNBOUND で止めます。値を調べること自体が例外になるとき（Proxy の罠など）は INSPECTION_FAILED です。getter は呼びません。",
        "結果を、別に書いた判定の手本と突き合わせる 1,000,000 回のファズ（P-01 の代役のランダムな故障を含む）で、不一致と fail-open が 0 であることを確かめました。本物の P-01 との結線も確かめました。"
      ],
      "what_it_does_not_do": [
        "どの結果にも adoption: NOT_ADOPTED・approval: false・authority: NOT_DERIVED・claim: NOT_DERIVED が付きます。PROPOSE の答えでも採用・保存・外への適用はせず、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
        "合う・合わないの判断は P-01 のままで、この部品は P-01 の答えを読むだけです。主張の正しさや根拠を格上げせず、proposal の中身はそのまま返します。",
        "P-01 が正しく働くことは、P-01 の検査に任せています。ここで確かめたのは、本物の P-01 との結線までです。",
        "binding に試行（attempt）の ID は入れません。AI は実行側の試行を知らないため、要求と試行の対応はこの部品の外で扱います。",
        "判断の schema の中身・採用・保存・本人の確認は、この部品の外で使う側が決めます。答えも残しません（状態は設定だけで、凍結されています）。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定の AI の出力の parser の使い方の雛形ではありません。"
      ],
      "integration": "src/intake.js の createTypedAnswerIntake({ contract_check, schema_digest, schema_id, proposal_schema }) で instance を作り、instance.intake(answer_text, expected_binding) を呼びます。port には P-01 の checkContract・schemaDigest（同期）を渡します。返すのは { ok: true, errors: [], status, binding, proposal, questions, reason, schema_digest } か { ok: false, errors: [{ code, detail? }] } で、投げません。副作用・状態・資格情報は無く、依存は組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    },
    {
      "id": "P-22",
      "category": "P/アプリ基盤",
      "kind": "Kernel",
      "title": "採用の版の台帳 — 採用の版の台帳の部品（Kernel）",
      "summary": "採用の版の台帳の部品（Kernel）は、版つきの項目の「候補 → 人の確認 → 採用 → 旧採用・失効」の移り変わりと確認の記録を、渡された台帳の上で 1 操作ずつ当て（applyAdoption）、今の条件と影響の入力から項目ごとの事実を返す（reviewAdoption）単独の部品です。新しい版（訂正を含む）は必ず候補で入り、採用はその版に合う人の確認があるときだけです。旧版・失効したもの・履歴は消しません。inference と advice はいつも null です。",
      "problem": "「採用中」の裏で、誰がどの版を確かめたかは見えません。この部品は、AI が確認・採用・失効すること、訂正を自動で採用すること、古い版の送り直しをより新しい採用の上に採用すること、旧採用や履歴を消すこと、つじつまの合わない台帳を使うことを防ぎます。",
      "use_when": [
        "新しい版を、その項目の今までのどの版より大きい版として、候補（CANDIDATE）で台帳に足したいとき（PROPOSE）。source は PERSON か AI で、AI の提案は候補の欄に入り、確認の欄とは別です。",
        "人（actor_kind: PERSON）の確認を、候補と中身の digest・binding のどの欄も同じときだけ記録したいとき（CONFIRM）。同じ digest でも binding の欄が違えば別のものとして扱います。",
        "人の確認がある候補だけを採用として記録し、前の採用を旧採用（SUPERSEDED）にしたいとき（ADOPT）。候補か採用を失効（REVOKED）にしたいとき（REVOKE、reason_ref つき）。",
        "台帳の版（expected_ledger_revision）を添えて操作を当て、台帳が先に進んでいたら何も当てずに LEDGER_CONFLICT で受け取りたいとき。",
        "採用の版の binding の欄ごとに今の条件（context）と比べ、違う欄（changed）・今の値が無い欄（unknown）と、再確認の状態（CURRENT・RECONFIRM_REQUIRED・RECONFIRM_UNKNOWN・NOT_ADOPTED）を項目ごとに受け取りたいとき（reviewAdoption）。",
        "使う側が別に計算した影響（AFFECTED・NOT_AFFECTED・UNKNOWN）を impacts として渡し、項目ごとの impact（NOT_AFFECTED・REVOCATION_CANDIDATE・IMPACT_UNKNOWN・IMPACT_NOT_PROVIDED・NOT_ADOPTED）として写して受け取りたいとき。",
        "時計・乱数・環境の locale を使わず（時刻は呼び出し側が at で渡す）、例外を投げず、入力を書き換えずに、同じ入力から同じ事実が要るとき。"
      ],
      "do_not_use_when": [
        "台帳を保存したいとき。結果の新しい台帳は保存前の候補（committed: false）で、保存と原子的な比較は使う側の役目です。",
        "actor_ref・actor_kind が本当にその人かを確かめたいとき。本人の確かめ（認証）はせず、呼び出し側の申告として記録します。",
        "影響を計算させたい、影響の入力が正しいかを確かめたいとき。影響の入力は写すだけで、渡されていない影響は「影響なし」にせず IMPACT_NOT_PROVIDED にします。",
        "失効を実行させたいとき。REVOCATION_CANDIDATE は影響の入力の写しで、失効の実行ではありません。REVOKE の操作は人の操作として渡されたときだけ当てます。",
        "binding の欄の意味（どの欄が材料の版か）を部品に決めさせたいとき。欄の名前は使う側が決めます。",
        "上限（entries・confirmations・impacts 10,000 件、events 100,000 件）を超える台帳や、大きな台帳での速さが要るとき。確認の照合は件数の 2 乗で、大きな台帳での速さは確かめていません。",
        "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
      ],
      "what_it_checks": [
        "引数が素のオブジェクトでない・知らない名前や getter がある・欄が無いとき INVALID_INPUT、台帳の形が違う（schema・版の数・digest・binding・source・state、確認の actor_kind が PERSON でない、出来事の op など）とき INVALID_LEDGER、件数の上限を超えるとき TOO_MANY_ENTRIES で止めます。",
        "台帳の内側のつじつま（ledger_revision と出来事の数、seq の飛び、同じ項目と版の組、採用が 2 つ、在る候補と合わない確認、確認の無い採用・旧採用）が合わないとき、LEDGER_INCONSISTENT で止め、その台帳を使いません。",
        "操作の形が違うとき（知らない op、その op に無い名前・getter、欠け、空の item_id、0・小数・文字列・安全でない整数の版、大文字の digest、binding の欄の名前・値・数の違い、知らない source、actor_ref・at の欠け）、INVALID_OPERATION で止めます。",
        "CONFIRM・ADOPT・REVOKE の actor_kind が PERSON でないとき CONFIRMER_NOT_PERSON、CONFIRM の digest か binding の欄が候補と違うとき CONFIRMATION_MISMATCH（path は最初の違い）、ADOPT の版に人の確認が無いとき NOT_CONFIRMED で止めます。",
        "PROPOSE の版が今までの版より大きくないとき REVISION_NOT_NEWER、項目と版の組が無いとき UNKNOWN_ENTRY、CONFIRM・ADOPT の相手が候補でないとき NOT_A_CANDIDATE、より新しい採用があるとき STALE_REVISION、REVOKE の相手が旧採用か失効済みのとき NOT_REVOCABLE で止めます。",
        "context の形が違うとき INVALID_CONTEXT、impacts の形が違う・同じ版が 2 回あるとき INVALID_IMPACTS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。止めるときは最初の 1 件のコードと位置（path。JSON Pointer）を返します。",
        "結果を、線形の探索だけで書いた状態のモデルと 1 操作ずつ突き合わせる 1,000,000 回のファズで、不一致と fail-open が 0 であることを確かめました。"
      ],
      "what_it_does_not_do": [
        "返すのは台帳の移り変わりと項目ごとの事実だけです。inference と advice はいつも null で、判断をしません。ADOPTED は「人が確認した版を採用として記録した」事実で、内容の正しさや承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
        "訂正（新しい版）を自動で採用しません。どの操作も、項目・確認・出来事を消しません。",
        "より新しい採用が失効した後は、古い確認済みの候補を人が採用し直せます。STALE_REVISION になるのは、より新しい ADOPTED があるときだけです。",
        "台帳の保存・原子的な比較・本人の確かめ・失効の実行・影響の計算は、この部品の外で使う側が行います。影響の計算の結果は impacts として受け取り、意味を作り変えません。",
        "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
        "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
        "特定のワークフローエンジンの使い方の雛形ではありません。"
      ],
      "integration": "src/adoption-ledger.js の applyAdoption({ ledger, operation }) と reviewAdoption({ ledger, context, impacts }) を呼びます（どちらも投げません）。空の台帳は schema \"erabazu.kernel/adoption-ledger/v1\"・ledger_revision 0・空の配列 3 つです。返すのは { ok: true, errors: [], facts }（新しい台帳と committed: false と transition、または items。inference・advice は null）か { ok: false, errors: [{ code, path }] } です。port は無く、依存は Node.js の組み込みだけです。",
      "license": {
        "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
        "text_url": "licenses/LICENSE_JA.md",
        "english_text_available": false,
        "other_licenses": []
      },
      "test_summary": {
        "status": "工房の技術検査PASS",
        "fuzz_runs": 1000000,
        "mismatches": 0,
        "fail_open": 0,
        "node_version": "v24.21.0",
        "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
      }
    }
  ],
  "languages": {
    "ja": [
      {
        "id": "A-01",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "トークン検証 — トークン検証継手（Tsugite）",
        "summary": "トークン検証継手（Tsugite）は、裏方の認証基盤（OIDC/OAuth・JWT）から受け取ったトークンの主張（claims）を検め、alg・aud・iss・exp と、署名が検証済みかどうかの印（signature_valid）のどれか一つでも合わなければ通さない継手（つぎて）です（fail-closed）。署名そのものの検証はしません。signature_valid は呼び出し側が検証して渡します。",
        "problem": "「ログインできた」で終わってしまい、トークンの検証の中身が見えないことを防ぎます。alg: none や許可していない alg、別の相手向けの aud、pin していない発行者、期限切れのトークンを、判定の段階で必ず止めるための検めです。",
        "use_when": [
          "OIDC/OAuth のトークンの alg・aud・iss・exp を、決めた規則どおりに検めたいとき。",
          "alg: none や許可していない alg（例: HS256）のトークンを確実に拒否したいとき。",
          "署名の検証は自分の側（pin した JWKS・鍵）で済ませ、その結果と claims の突き合わせを、通信のない純粋な関数で判定したいとき。",
          "トークンの正当性の判定結果を、認可の判定（policy-facts）や監査・来歴（token-facts）の核へ継ぎたいとき。"
        ],
        "do_not_use_when": [
          "トークンの署名そのものを検証してほしいとき。この継手は署名を検証しません。signature_valid（真偽値）を呼び出し側から受け取り、それが検証済みでなければ拒否するだけです。",
          "IdP や JWKS の取得までまかせたいとき。取得は含みません。",
          "nbf・iat・kid を検めたいとき、または aud が配列のトークンを扱いたいとき。aud は 1 つの値として照合し、配列の aud は扱いません。",
          "認可（誰に何を許すか）を決めたいとき。認可の判定は policy-facts 核へ渡します。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "alg を検めます。alg: none と、許可一覧（allowed_algs）に無い alg（例: HS256）は拒否します。",
          "aud を検めます。許可した aud（allowed_aud）と一致しない aud（aud の混同）は拒否します。",
          "iss を検めます。pin した iss（allowed_iss）以外の発行者は拒否します。",
          "exp を検めます。猶予（leeway_seconds、既定 0）を含めて期限を過ぎたトークンと、exp が無いトークンは拒否します。",
          "signature_valid を検めます。署名が検証済みでない（signature_valid が真でない）トークンは拒否します。",
          "どれか一つでも外れたら { ok: false, errors: [...] } を返し、通しません（fail-closed）。"
        ],
        "what_it_does_not_do": [
          "トークンの署名の実際の検証（pin した JWKS・鍵による検証）はしません。signature_valid は呼び出し側が検証して渡す値で、この継手はその値を信じて判定します。",
          "IdP・JWKS の取得はしません。通信をしない判定だけの部品で、port もありません。",
          "nbf・iat・kid は検めません。配列の aud も扱いません。",
          "検めるのは claims の alg・aud・iss・exp・signature_valid の 5 つだけです。カタログの一言にある「失効」は、この継手では exp による期限切れの拒否（leeway 込み）として扱います。",
          "実 JWKS・鍵には、この部品の中ではつないでいません。配備するときに pin します。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "IdP の使い方の雛形ではありません。"
        ],
        "integration": "src/guards.js の verifyClaims(cfg, claims, now) を呼びます（純粋な関数で、通信はしません）。cfg には allowed_algs・allowed_aud・allowed_iss・leeway_seconds を、claims には { alg, aud, iss, exp, signature_valid } を、now には検証時刻（秒）を渡すと、{ ok: true, errors: [] } か { ok: false, errors: [...] } を返します。利用側が用意するもの: トークンの署名の検証（pin した JWKS・鍵）とその結果の signature_valid、IdP・JWKS の取得。port は無く、外部への副作用はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-02",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "認可判定の参照 — 認証・認可の拡張継手（Tsugite）",
        "summary": "認証・認可の拡張継手（Tsugite）は、A-02 では、外の判断点が出した許可の判断を参照として、pin した許可の方針で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。許可の評価（RBAC/ABAC）はしません。判断は外のもので、PERMIT を受け取っても許可の権限は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "外の判断点の許可の判断を受け渡すとき、pin していない方針の判断を運んでしまうこと、判断を名乗る receipt から許可の権限を導いてしまうこと、そして判断を受け取ったことを承認と取り違えることを防ぎます。",
        "use_when": [
          "外の判断点が出した許可の判断を、参照（decision_ref）として、pin した許可の方針（policy_refs）の範囲でだけ基盤へ運びたいとき。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には decision・decision_ref・permission_authority が添えられます。",
          "判断の受け渡しで外への効果を起こしたくないとき。許可の判断の参照の副作用は NONE です。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "RBAC/ABAC で「この人はこれをしてよいか」を評価させたいとき。この継手は外の判断点の判断を参照として運ぶ継手で、許可の評価の機能は含みません。",
          "受け取った PERMIT を許可の権限として使いたいとき。PERMIT を受け取っても許可の権限は導かず、permission_authority を導いたと名乗る receipt は受理しません。",
          "判断が正しいかを確かめたいとき。許可の判断そのものは確かめません（外の判断と基盤の役目）。",
          "すぐに使える判断点がほしいとき。実の基盤（どの判断点か）は含まず、未定です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "A-02 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
        ],
        "what_it_checks": [
          "方針を検めます。pin していない許可の方針の要求は送りません。空の一覧は何も許しません。",
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "許可の権限を導いたと名乗る receipt は受理しません。",
          "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "許可の評価をしません。この継手は外の判断点の判断を参照として運ぶ継手で、RBAC/ABAC の評価の機能は含みません。許可の判断は外の判断点、許可の方針の事実は核（policy-facts）の役目です。",
          "PERMIT は許可の権限ではありません。判断を受け取ったことから、許可・権限を導きません。",
          "判断を受け取ったことは承認ではありません（approval: false）。",
          "実の基盤（どの判断点か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "直接の識別子のすべてを見分けるものではありません。検出はメールアドレスと電話番号の代表的な形だけです。",
          "特定の IdP や判断点の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に AuthzDecisionRefRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: decision・decision_ref・permission_authority。cfg には pin する一覧 6 つ（policy_refs ほか）がどれも要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-03",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "セッション管理 — セッション・失効伝播継手（Tsugite）",
        "summary": "セッション・失効伝播継手（Tsugite）は、session store への開く（OPEN）・再開（RESUME）・終了（TERMINATE）・失効（REVOKE）の操作を、期限・同時数・既知の状態を検めてから送り、store が返す受領（SessionReceipt）を照合してから session の状態を進める継手（つぎて）です。一つでも合わなければ、送らず、session を使える扱いにしません（fail-closed）。",
        "problem": "「ログイン状態」は当然のものと思われがちですが、期限を過ぎた session、同時数の上限を超えた session、失効・終了したはずの session が使われ続けることがあります。この継手は、session の発行・再開・終了・失効を検めて送り、store の受領を照合できない限り、その session を使える扱いにしないための検めです。",
        "use_when": [
          "session の発行・再開・終了・失効を、外部の session store に対して行い、その結果を照合してから状態を進めたいとき。",
          "session の最長の長さ（max_session_seconds）と同時に使える数の上限（max_concurrency）を、開く時点で必ず守らせたいとき。",
          "ある時刻にある session を使ってよいかを、照合済み・開いている・期限内・未確定の操作が無い、のすべてがそろうときだけ「使ってよい」と判定したいとき。",
          "store の障害で結果が分からないとき、その session を解決まで使えない扱いにしたいとき。"
        ],
        "do_not_use_when": [
          "session があることを、本人確認や権限の代わりにしたいとき。session は本人確認でも権限でもありません（authority: NOT_DERIVED、approval: false）。",
          "トークンそのものを検証したいとき。それは A-01（token-verify-tsugite）の役目で、この継手は session の操作と失効の伝播だけを扱います。",
          "すぐに使える session store がほしいとき。実 session store は含まず、どれにつなぐかはまだ決まっていません。",
          "session の期限や同時数の上限そのものを決めてほしいとき。決定は核（session-facts・policy-facts・timestamp-facts）の役目で、この継手は上限と状態を受け取って検めるだけです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "同時数を検めます。同時に使える session の上限（max_concurrency）を超える OPEN は送りません。",
          "期限と時刻を検めます。期限の巻き戻しと時刻の巻き戻しを拒否します。OPEN には期限（expiry）と同時数の上限（concurrency_limit）を必須とし、OPEN 以外では期限・同時数を変えさせません。",
          "失効・終了した session の再開を拒否します。store が失効後に RESUMED と返したら、受理せず停止します。",
          "受領（SessionReceipt）を照合します。操作と食い違う状態（session_state は op から決まる。NONE・UNKNOWN も照合しない）、別の session の受領、request_digest の偽りがあれば受理せず、以後は HALTED にします。",
          "権限や本人確認を持ち込む受領を拒否します。authority を導いたもの、検証者の無い本人確認済み（VERIFIED）は受理しません。",
          "SecretRef の隣に生の秘密が置かれた要求を拒否します。",
          "失効（REVOKE）の受領には伝播の参照を必須とし、pin した伝播先すべてに証拠つきで届くまでは「未完了」（REVOKED_PROPAGATION_INCOMPLETE）とします。詳しくは A-08（失効伝播）で扱います。",
          "store の障害は UNKNOWN とし、その session は解決まで使えない扱いにします。"
        ],
        "what_it_does_not_do": [
          "session は本人確認でも権限でもありません。どの結果にも authority: NOT_DERIVED と approval: false が付きます。",
          "トークンの検証はしません。A-01（token-verify-tsugite）の役目です。",
          "実 session store と伝播先（各サービスの失効受付）を含みません。どれにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "session の上限や規則を決めません。決定は核（session-facts・policy-facts・timestamp-facts）の役目で、この継手は受け取った上限と状態を検めるだけです。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。なお、OPEN の必須項目などで契約より厳しくした点があります。",
          "A-03 と A-08 は契約上 1 つの部品です。ID ごとの行（A-03・A-08）が、この部品を指します。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "session store の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createSessionClient({ cfg, port }) の apply に SessionOpRequest（OPEN・RESUME・TERMINATE・REVOKE）を渡すと、APPLIED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED・REVOKED_EVERYWHERE・REVOKED_PROPAGATION_INCOMPLETE を返します（approval: false 付き）。isUsable(session_ref, now) で使えるかを判定。利用側が用意するもの: store と伝播の port（apply・propagation）。副作用: EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-04",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "多要素・ステップアップ（問いかけと照合を運ぶ） — 認証・認可の拡張継手（Tsugite）",
        "summary": "認証・認可の拡張継手（Tsugite）は、A-04 では、追加認証の問いかけ（CHALLENGE）と照合（VERIFY）の要求を、pin した要素と資格情報の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。照合は、この継手が同じ本人へ送った問いかけに対してだけ行います。追加認証を満たしても許可や権限ではありません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "重要な操作の直前に追加認証を挟むとき、送っていない問いかけや別の本人の問いかけに対して照合してしまうこと、token の形の値をそのまま運んでしまうこと、そして追加認証を満たしたことを許可と取り違えることを防ぎます。",
        "use_when": [
          "重要な操作の直前だけ、追加認証の問いかけを基盤へ送り、その答えを照合したいとき。",
          "追加認証の要素を、許す要素の一覧（allowed_factors）の範囲にとどめたいとき。",
          "照合を、この継手が同じ本人へ送って CHALLENGED と照合した問いかけに限りたいとき。状態は CHALLENGE→CHALLENGED・FAILED・UNKNOWN、VERIFY→SATISFIED・FAILED・UNKNOWN です。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には step_up_state・satisfied・authorization が添えられます。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "追加認証を満たしたことを、操作の許可として使いたいとき。満たしても許可や権限ではなく、許可を導いたと名乗る receipt は受理しません。",
          "追加認証が正しいか、資格情報が有効かを確かめたいとき。これらは確かめません（外の判断と基盤の役目）。",
          "秘密や token の値そのものを運びたいとき。秘密・token の bytes は運ばず、参照だけです。",
          "すぐに使える追加認証の基盤がほしいとき。実の基盤（どの IdP か）は含まず、未定です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "A-04 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
        ],
        "what_it_checks": [
          "要素を検めます。許す一覧にない要素、契約にない要素の要求は送りません。空の一覧は何も許しません。",
          "照合の相手を検めます。この継手が送っていない問いかけ、別の本人への問いかけ、失敗した問いかけに対する照合は送りません（REFUSED）。",
          "資格情報の参照を検めます。pin していない資格情報の要求は送りません。資格情報の参照・assertion_ref が bytes や token の形なら、pin されていても送りません。",
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "追加認証から許可を導いたと名乗る receipt は受理しません。",
          "receipt の request_id・request_digest・参照が要求と一致しないもの、外への効果を増やすもの、効果が UNKNOWN のものは受理しません。受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "追加認証を満たしたことは、許可や権限ではありません。許可の判断は外の判断点と人・組織の役目で、継手は送る前と受け取った後の検めだけをします。",
          "追加認証の正しさ・資格情報の有効さは確かめません（外の判断と基盤の役目）。",
          "満たしたことは承認ではありません（approval: false）。",
          "実の基盤（どの IdP か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。直接の識別子の検出もメールアドレスと電話番号の代表的な形だけです。",
          "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に StepUpAuthRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: step_up_state・satisfied・authorization。cfg に pin する一覧 6 つが要ります。利用側が用意するもの: port（submit、任意で lookup）。副作用: 問いかけ EXTERNAL_REVERSIBLE、照合 NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-05",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "サービス間認証（ワークロードの認証を運ぶ） — 認証・認可の拡張継手（Tsugite）",
        "summary": "認証・認可の拡張継手（Tsugite）は、A-05 では、ワークロード（サービス）の認証の要求を、pin したワークロードと資格情報の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。秘密や token の bytes は運ばず、参照だけを運びます。認証できたことから権限は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "サービス同士の認証を基盤へ運ぶとき、pin していないワークロードや資格情報で要求を出してしまうこと、資格情報の bytes や token の形の値をそのまま運んでしまうこと、そして認証できたことを権限と取り違えることを防ぎます。",
        "use_when": [
          "サービス（ワークロード）同士の認証の要求を、pin したワークロード（workload_refs）と資格情報の参照（credential_refs）の範囲でだけ基盤へ運びたいとき。",
          "資格情報を bytes ではなく参照だけで扱いたいとき。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には auth_state・authority が添えられます。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "Workload Identity や mTLS の仕組みそのものがほしいとき。この継手はワークロードの認証の要求を参照で運ぶ継手で、その仕組みの機能は含みません。",
          "ワークロードが認証できたことを、権限として使いたいとき。権限は導かず、ワークロードの権限を導いたと名乗る receipt は受理しません。",
          "資格情報が有効かを確かめたいとき。資格情報の有効さは確かめません（外の判断と基盤の役目）。",
          "資格情報や token の値そのものを運びたいとき。秘密・token の bytes は運ばず、参照だけです。",
          "すぐに使える認証の基盤がほしいとき。実の基盤は含まず、未定です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "A-05 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
        ],
        "what_it_checks": [
          "ワークロードを検めます。pin していないワークロードの要求は送りません。空の一覧は何も許しません。",
          "資格情報の参照を検めます。pin していない資格情報の要求は送りません。資格情報の参照・assertion_ref が bytes や token の形なら、pin されていても送りません。",
          "参照の形を検めます。パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "ワークロードの権限を導いたと名乗る receipt は受理しません。",
          "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "Workload Identity・mTLS の仕組みそのものは含みません。ワークロードの認証の要求を、参照で検めて運ぶだけです。",
          "認証できたことは権限ではありません。身元や資格情報の状態から、許可・権限を導きません。",
          "資格情報の有効さは確かめません（外の判断と基盤の役目）。認証できたことは承認でもありません（approval: false）。",
          "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。",
          "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に WorkloadAuthRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: auth_state・authority。cfg に pin する一覧 6 つ（workload_refs ほか）が要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE か EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-06",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "APIキー発行・回転（参照で運ぶ・鍵を開示させない） — 認証・認可の拡張継手（Tsugite）",
        "summary": "認証・認可の拡張継手（Tsugite）は、A-06 では、API キーの発行（ISSUE）・回転（ROTATE）・失効（REVOKE）の要求を、pin した鍵と秘密の置き場所の参照で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。鍵の bytes は運ばず、鍵の中身を開示する receipt は受理しません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "外部連携の API キーを払い出し・回し・失効させるとき、pin していない鍵や置き場所を扱ってしまうこと、鍵の bytes をそのまま運んでしまうこと、基盤が鍵の中身を返してくるのを受け取ってしまうこと、そして鍵の状態を権限と取り違えることを防ぎます。",
        "use_when": [
          "API キーの発行・回転・失効の要求を、pin した鍵（key_refs）と鍵の置き場所（secret_refs）の範囲でだけ基盤へ運びたいとき。",
          "新しい鍵を、中身ではなく置き場所の参照（secret_ref）だけで受け渡したいとき。secret_ref は ISSUE・ROTATE のときだけ必須で、状態は ISSUE→ISSUED_REF・ROTATE→ROTATED_REF・REVOKE→REVOKED か FAILED です。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には key_state・key_material_disclosed が添えられます。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "鍵の中身を受け取りたいとき。鍵の中身を開示する receipt は受理しません。",
          "鍵の保管そのものがほしいとき。この継手は発行・回転・失効の要求を参照で基盤へ運ぶ継手で、鍵の保管は含みません（どの鍵の保管かは未定）。",
          "鍵が有効であることを、許可や権限として使いたいとき。資格情報が有効でも、許可や権限ではありません。",
          "資格情報が有効かを確かめたいとき。資格情報の有効さは確かめません（外の判断と基盤の役目）。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "A-06 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
        ],
        "what_it_checks": [
          "鍵と置き場所を検めます。pin していない鍵、pin していない秘密の置き場所の要求は送りません。空の一覧は何も許しません。",
          "secret_ref を検めます。鍵の形の secret_ref は、pin されていても送りません。REVOKE に secret_ref を付けた要求も送りません。",
          "鍵の中身を開示する receipt は受理しません。",
          "参照の形を検めます。パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "鍵の中身を運ばず、受け取りません。扱うのは鍵と置き場所の参照だけです。",
          "鍵の状態から許可・権限を導きません。資格情報の有効さも確かめません（外の判断と基盤の役目）。",
          "発行・回転・失効ができたことは承認ではありません（approval: false）。",
          "実の基盤（どの鍵の保管か）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "すべての秘密を見分けるものではありません。token や鍵の形の検出は代表的な形だけを見る網で、本当の守りは pin した参照の一覧です。",
          "特定の鍵の保管サービスの使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の submit(request) に ApiKeyLifecycleRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: key_state・key_material_disclosed。cfg に pin する一覧 6 つが要ります。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-07",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "委任・代理（on-behalf-of）の参照 — 認証・認可の拡張継手（Tsugite）",
        "summary": "認証・認可の拡張継手（Tsugite）は、A-07 では、人・組織から受け取った根拠がある委任の参照だけを検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。ERABAZU は委任の権限を作りません。委任の権限は、外の人・組織の根拠からだけ来ます。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "「本人に代わって」の委任を基盤へ運ぶとき、人・組織の根拠の無い委任を運んでしまうこと、ERABAZU 自身を委任の出どころにしてしまうこと、そして委任の権限を作ったと名乗る receipt を受け取ってしまうことを防ぎます。",
        "use_when": [
          "人・組織が出した委任の根拠を recordAuthorityEvidence(evidence_ref, origin) で受け取り（origin は HUMAN か ORGANIZATION だけ）、その根拠がある委任の参照だけを基盤へ運びたいとき。",
          "委任の当事者を、メールアドレス・電話番号ではなく仮名の参照だけで扱いたいとき。",
          "委任の受け渡しで外への効果を起こしたくないとき。委任の参照の副作用は NONE です。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には delegation_state・authority_origination が添えられます。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "委任の権限を ERABAZU に作らせたいとき、または権限の受け渡しそのものをさせたいとき。この継手は委任の参照を運ぶ継手で、ERABAZU は委任の権限を作りません。",
          "人・組織の根拠が無いまま委任を運びたいとき。その要求は送りません（REFUSED）。",
          "委任が本物かを確かめたいとき。委任の根拠の中身は検めず、人・組織から受け取った参照かだけを見ます。",
          "すぐに使える基盤がほしいとき。実の基盤は含まず、未定です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "A-07 だけの部品を求めるとき。この継手は A-02・A-04・A-05・A-06・A-07 を契約上 1 つの部品として担い、試験と検査も共通です。A-01 は token-verify 継手、A-03・A-08 は session 継手が担います。"
        ],
        "what_it_checks": [
          "委任の根拠を検めます。根拠の無い委任、recordAuthorityEvidence で受け取っていない根拠による委任、出どころを ERABAZU とした委任は送りません。",
          "委任の権限を作ったと名乗る receipt は受理しません。",
          "当事者の参照と参照の形を検めます。メールアドレス・電話番号の形の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。request_digest を偽った receipt も受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "ERABAZU は委任の権限を作りません。委任の権限は外の人・組織の根拠からだけ来ます。この継手は委任の参照を運ぶ継手で、権限の受け渡しの機能は含みません。",
          "委任の根拠の中身（本物の委任か）は検めません。人・組織から受け取った参照かだけを見ます。",
          "委任を運べたことは承認ではありません（approval: false）。",
          "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "直接の識別子のすべてを見分けるものではありません。検出はメールアドレスと電話番号の代表的な形だけです。",
          "特定の IdP の使い方の雛形ではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/identity-ext/v1 でつなぎます。createIdentityExtClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で人・組織の根拠を受け取ってから、submit(request) に DelegationRefRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。DONE の result: delegation_state・authority_origination。利用側が用意するもの: 基盤への port（submit、任意で lookup）。副作用は NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-08",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "失効伝播 — セッション・失効伝播継手（Tsugite）",
        "summary": "セッション・失効伝播継手（Tsugite）は、session の失効（REVOKE）を store へ送って受領を照合し、pin した伝播先すべてから証拠つきの到達の受領がそろうまで失効を「未完了」とする継手（つぎて）です。未完了のあいだも、その session は使えない扱いにします（fail-closed）。",
        "problem": "「ログアウトしたのに残る」事故を防ぎます。失効を一か所で受け付けただけで「全体に行き渡った」と扱ってしまうこと、そして証拠の無い到達の知らせや pin していない先からの知らせを信じてしまうことを防ぎ、失効を全系へ行き渡らせたかを照合できる形で確かめるための検めです。",
        "use_when": [
          "ログアウトや失効を、決めた伝播先（propagation_targets）すべてに届けたかどうかを、到達の受領で確かめたいとき。",
          "どの伝播先にまだ届いていないかを知りたいとき。未完了のときは、届いていない先（missing_targets）を添えて返します。",
          "受領の不整合で停止している最中でも、失効だけは止めずに送りたいとき。この継手が知らない session の失効も受け付けます。",
          "失効した session が、その後に再開されないようにしたいとき。"
        ],
        "do_not_use_when": [
          "伝播先への失効の送り届けそのものをまかせたいとき。到達は、port（propagation）が返す到達の受領で検めるだけで、伝播先の実装は含みません。",
          "すぐに使える伝播先や session store がほしいとき。実 session store と伝播先（各サービスの失効受付）は未定です。",
          "トークンそのものを失効・検証したいとき。トークンの検証は A-01（token-verify-tsugite）の役目で、この継手は session の操作と失効の伝播だけを扱います。",
          "失効が行き渡ったことを、本人確認や権限・承認として扱いたいとき。どの結果も authority: NOT_DERIVED・approval: false です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "伝播先の欠けを検めます。pin した伝播先のどれか一つでも到達の受領が無ければ、完了とせず REVOKED_PROPAGATION_INCOMPLETE を返します。",
          "到達の証拠を検めます。証拠（evidence_ref）の無い到達の知らせは、届いたとは認めません。",
          "伝播先の pin を検めます。pin していない先からの到達の知らせは、届いたとは認めません。",
          "失効の受領を照合します。REVOKE の受領には伝播の参照を必須とし、状態の食い違い（REVOKE なら REVOKED）、別の session の受領、request_digest の偽りがあれば受理しません。",
          "失効後の残存を拒否します。失効・終了した session は再開せず、store が失効後に RESUMED と返したら受理せず停止します。",
          "失効が完了していても未完了でも、その session は使えない扱いにします。store の障害（UNKNOWN）のときも、解決まで使えない扱いです。"
        ],
        "what_it_does_not_do": [
          "伝播先へ失効を届ける仕組みそのものは持ちません。到達の受領の形（target_ref・session_ref・revoked・evidence_ref）は契約に無く、継手の側の定めです。",
          "実 session store と伝播先（各サービスの失効受付）を含みません。port の実装と pin する対象は未定で、接続するときに別の関門で決めます。",
          "トークンの検証・失効はしません。トークンの検証は A-01（token-verify-tsugite）の役目です。",
          "失効が行き渡ったことは、本人確認でも権限でもありません（authority: NOT_DERIVED、approval: false）。",
          "A-08 は A-03（セッション管理）と契約上 1 つの部品です。ID ごとの行（A-03・A-08）が、この部品を指します。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "session store の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createSessionClient({ cfg, port }) の apply に REVOKE の SessionOpRequest を渡すと、REVOKED_EVERYWHERE か REVOKED_PROPAGATION_INCOMPLETE（missing_targets 付き）などを返します（authority: NOT_DERIVED・approval: false 付き）。伝播先は cfg の propagation_targets に pin します。利用側が用意するもの: store の port（apply）と、到達の受領の一覧を返す port（propagation）。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-09",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "組織ドメインの管理権の照会 — 組織のドメインの管理権の照会の継手（Tsugite）",
        "summary": "組織のドメインの管理権の照会の継手（Tsugite）は、組織のドメインに置かれた印を、DNS TXT（`_kokoroamu.<domain>`）か HTTPS の決まった場所（`/.well-known/kokoroamu-org.json`）で port に照会させ、返った receipt（OrgDomainControlReceipt）を契約どおりに照合して、受け取ったままの答えを運ぶ継手（つぎて）です。照会できた・印が見つかったことを、管理権の証明として扱いません（fail-closed）。",
        "problem": "「ドメインを確認しました」と言うとき、どこを読み、どの答えを信じたかが見えなくなることを防ぎます。名前や URL はドメインだけから組み立て、転送・TLS・DNSSEC・大きさを検め、受け取ったままの答えを digest とともに運びます。照会を管理権の証明と取り違えず、答えを別の目的に使い回しません。",
        "use_when": [
          "組織のドメインに置かれた印を、DNS TXT（`_kokoroamu.<domain>`、method DNS_TXT）か HTTPS の決まった場所（`/.well-known/kokoroamu-org.json`、method HTTPS_WELL_KNOWN）で照会し、その答えをあとで読む側へ渡したいとき。",
          "呼ぶ側にはドメインだけを渡させ、DNS の名前・URL・port 番号・path は呼ぶ側に決めさせたくないとき。",
          "1 つのドメインを、目的ごとに別々の照会で使いたいとき（今の目的の表は `seat8`）。目的は依頼と答えの両方に入り、答えは別の目的に使い回しません（reusable_for_other_purpose false）。",
          "TXT の行を受け取った順のまま、HTTPS の本文を bytes（base64）のまま、それぞれの digest とともに残したいとき。"
        ],
        "do_not_use_when": [
          "印を読んで、目的と鍵 ID を照合し、期限を決めたいとき。それは A-10 の部品（org-domain-claim-primitive）と規則・人の役目で、この継手は照会だけをします。",
          "照会の結果を、そのまま管理権の証明として使いたいとき。この継手の答えは、管理権を認めるものではありません（ownership_decision NOT_DERIVED）。",
          "任意の URL・port 番号・path を照会したいとき。要求にその欄は無く、受け取りません。",
          "すぐに使える照会の仕組みがほしいとき。実の DNS・HTTPS への照会は 0 回で、port の実装・resolver・信頼する CA の一覧・設定の値は、接続するときに別の関門で決めます。",
          "照会の記録を長く残したいとき。記録はこの継手の中だけで、再起動で消えます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定と目的を検めます。要求の contract_pin が設定 contract_schema_digest と違うとき、設定が無いとき、目的が allowed_purposes に無いとき、resolver が resolver_profile_refs に無いときは送りません（REFUSED）。空の表は何も許しません。",
          "receipt の名前と URL が、ドメインから組み立てたものと同じことを照合します。答えの目的・ドメイン・鍵 ID が依頼と違う receipt は受理しません。",
          "答えの中身を照合します。TXT の順・行数・digest、本文の大きさ・digest・content type（JSON か text、charset=utf-8 だけ）が合わない答えは受理しません。受け取りと要求の文字列は、正しい Unicode のときだけ受け取ります。",
          "転送と TLS を検めます。別のホスト・http・上限を超える転送をたどった答えは受理しません（たどらなかった転送は REDIRECT_REFUSED）。TLS の検めが外れた FOUND・NOT_FOUND、DNSSEC の BOGUS を含む答え、TIMEOUT の途中の答えも受理しません。",
          "つないだアドレス（connected_address）を検めます。IANA の特別用途アドレスの登録の、そのアドレスを含むいちばん狭い行で global のものだけを受け取ります。NAT64 の prefix の中へつないだ答えは、RFC 6052 で取り出した中の IPv4 も global のときだけ受け取ります。",
          "判断しない印と効果を照合します。claim_evaluation NOT_PERFORMED・ownership_decision NOT_DERIVED・side effect NONE を守らない receipt は受理せず、照合できない receipt のあとは停止します（RECEIPT_REJECTED のあとは HALTED）。",
          "同じ request_id の違う中身は送りません。port の障害や時間切れ（limits.timeout_ms と猶予 1 秒）は UNKNOWN とし、同じ要求を送り直して解きます（port.lookup があればまずそれで）。"
        ],
        "what_it_does_not_do": [
          "印（`v=kokoroamu-org1`）を読む・目的と鍵 ID を照合する・期限を決める・管理権を認めることはしません。照会できた・印が見つかったことは、管理権の証明ではありません。",
          "解決したアドレスがすべて global だったか、cookie・資格情報を送らなかったか、名前と URL を実際にドメインから組み立てたかは確かめません（port の役目）。この継手が検めるのは、receipt の connected_address が global であることまでです。",
          "DNSSEC の SECURE・INSECURE、TLS の 3 つの検めの結果、TTL は port の申告で、この継手は形と組み合わせだけを検めます。",
          "global の範囲の表は、IANA の登録をページとして読んだ写しで、生の登録の bytes とは照らしていません。公開の前に生の CSV と照らし直す必要があります。",
          "local の NAT64 の prefix を知りません。nat64 を返さない port の答えでは、local の prefix の中に埋められた IPv4 が global かを照らせません。",
          "何も書きません（side effect NONE）。運べた・照会できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/org-domain-control/v1（OrgDomainControlRequest / OrgDomainControlReceipt）でつなぎます。createOrgDomainControlClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。利用側の用意: 照会の port（submit と任意の lookup）と cfg（contract_schema_digest・allowed_purposes・resolver_profile_refs）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-10",
        "category": "A/認証・認可",
        "kind": "Primitive",
        "title": "組織ドメインの印の読み取り — 組織のドメインの印の読み取りと照合の部品（Primitive）",
        "summary": "組織のドメインの印の読み取りと照合の部品（Primitive）は、会社（組織）が自分のドメインに置いた印（DNS TXT の `_kokoroamu.<domain>` の行、または HTTPS の `/.well-known/kokoroamu-org.json` の JSON 文書）を、照会で受け取った生の答えから読み、目的と鍵 ID の完全一致で照合して、期限つきの記録（claim）を作る単独の部品です。読めない行・ほかの用途の行は捨てずに事実として返します。",
        "problem": "印の書き方の揺れや期限切れは、表から見えません。この部品は、知らない欄・重なる欄・知らない版の印を読まず、目的は呼ぶ側の allowed_purposes にあるものだけを通します。1 つのドメインの確認が別の目的に流用されて権限が広がることを防ぎ、期限は観測の時刻と日数から決めます。",
        "use_when": [
          "DNS TXT の答え（txt_records）か、well-known の本文（body_base64・content_type）から、印（目的と鍵 ID）の一覧を読みたいとき（parseMarks）。",
          "ある目的・ある鍵 ID の印があるかを文字列の完全一致で確かめ、domain・method・purpose・key_id・observed_at・expires_at の記録（claim）を作りたいとき（checkClaim）。",
          "読めない行・ほかの用途の行（SPF など）を、黙って捨てずに理由つき（ignored の reason）で残したいとき。",
          "記録が期限の内かを、渡した時刻（at）で確かめたいとき（isClaimCurrent。外なら reason は NOT_YET_OBSERVED か EXPIRED）。",
          "記録から、attestation の属性（domain:<domain> と purpose:<purpose> をこの順に 1 つずつ）を作りたいとき（claimToAttestationAttributes）。",
          "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。目的の表は部品の中に持たず、呼ぶ側が allowed_purposes で渡します。"
        ],
        "do_not_use_when": [
          "印がそのドメインから来たこと（DNS の名前・DNSSEC・HTTPS の証明書・リダイレクト）を確かめたいとき。この部品は、渡された答えがそのドメインのものだとして読みます。照会とその検めは、照会の部品（A-09 の案）と呼ぶ側の役目です。",
          "ドメインの持ち主の本人性（個人・組織の実在）を確かめたいとき。この部品が読むのは、ドメインを管理していることの印だけです。",
          "大文字・末尾の点・前後の空白を含むドメインや IP アドレスを、直して受け取ってほしいとき。名前は直さずに INVALID_DOMAIN で断ります。",
          "国際化ドメインを Unicode の形で渡したいとき。xn-- の形だけを受け取り、punycode の中身は確かめません。",
          "有効期間を 90 日より長くしたいとき。valid_days は 1〜90 の整数だけで、外れると INVALID_VALID_DAYS で止めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "DNS TXT の行は、最初の欄がちょうど決まった版の名前（`v=kokoroamu-org1`）で、残りが purpose=<目的> と key=<鍵 ID> を 1 つずつのときだけ印として読みます。知らない欄・重なる欄・空の欄・欠けた欄・知らない版の行は読まず、ignored に理由（UNKNOWN_FIELD・DUPLICATE_FIELD・MISSING_FIELD・UNKNOWN_VERSION・MALFORMED_MARK など）を付けて返します。",
          "4096 文字を超える行は TOO_LONG、UTF-8 でない行（{ undecodable_base64 } の形）は復号せずに NOT_UTF8、文字列でない行は NOT_TEXT として ignored に入れます。txt_records が配列でない・64 個を超えるときは INVALID_RECORDS・TOO_MANY_RECORDS で止めます。",
          "well-known の本文は、content_type が application/json・text/plain（任意で ; charset=utf-8）でないとき UNSUPPORTED_CONTENT_TYPE、body_base64 が正準の base64 でない・復号して 64 KiB を超えるとき INVALID_BODY で止めます。UTF-8 でない・JSON でない・文書の形が違う・entries が 1000 個を超える本文は、NOT_UTF8・NOT_JSON・MALFORMED_DOCUMENT・TOO_MANY_ENTRIES の事実で返します。",
          "purpose が allowed_purposes に無いときは matched: false で、reasons に PURPOSE_NOT_ALLOWED を記します。同じ鍵 ID でも、ほかの目的の印では通さず KEY_UNDER_OTHER_PURPOSE を記します。",
          "目的と鍵 ID は文字列の完全一致で比べます。鍵 ID は大文字と小文字を区別し、Unicode の正規化・大文字小文字の畳み込みはしません。",
          "domain・purpose・key_id・allowed_purposes・observed_at・valid_days・marks の形が違うとき、INVALID_DOMAIN・INVALID_PURPOSE・INVALID_KEY_ID・INVALID_ALLOWED_PURPOSES・INVALID_OBSERVED_AT・INVALID_VALID_DAYS・INVALID_MARKS で止めます。ドメインは 242 文字まで、時刻は UTC の Z の形で 2000〜9998 年、marks は 2000 個までです。",
          "isClaimCurrent・claimToAttestationAttributes に渡す claim の形が違うとき（余分なキー・期限が observed_at より後でない・90 日を超える）、INVALID_CLAIM で止めます。at の形が違うときは INVALID_AT です。claimToAttestationAttributes だけは OrgDomainClaimError を投げます。",
          "入力がオブジェクトでないとき INVALID_INPUT、source が DNS_TXT・HTTPS_WELL_KNOWN のどちらでもないとき UNKNOWN_SOURCE で止めます。止めたときは { ok: false, errors: [{ code }] } を返します。"
        ],
        "what_it_does_not_do": [
          "matched: true は、渡された答えの中にこの目的とこの鍵 ID の印があったという事実だけです。ドメインの持ち主の本人性、印がそのドメインから来たこと、attestation に署名してよいことは示しません。署名するかは呼ぶ側が決めます。",
          "DNS・HTTPS の照会はしません。外の境界・状態・乱数を持たず、時計も読みません（時刻はすべて入力）。",
          "目的の表は持ちません。どの目的を許すか（allowed_purposes）は呼ぶ側が渡します。",
          "JSON の重なるキーは JSON.parse のとおり最後の値を使い、重なりを見つけて断ることはしません。",
          "{ undecodable_base64 } の値は復号しません。正準の base64 か・本当に UTF-8 でないかは確かめず（受け取りの側で検めること）、その形の行を印として読みません。",
          "記録（claim）ができたことは、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "DNS や HTTPS の照会の雛形ではありません。"
        ],
        "integration": "src/org-domain-claim.js の parseMarks・checkClaim・isClaimCurrent（投げない）と claimToAttestationAttributes（投げる）を呼びます。parseMarks は txt_records か body_base64・content_type を受け取り marks と ignored を返します。checkClaim は domain・purpose・key_id・allowed_purposes・marks・observed_at・valid_days を受け取り matched・reasons・claim を返します。答えの形は契約 org-domain-control/v1 に合わせています。副作用・状態・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "A-11",
        "category": "A/認証・認可",
        "kind": "Component",
        "title": "組織ドメインの管理権の確かめ — 組織のドメインの管理権の確かめの部品（Component）",
        "summary": "組織のドメインの管理権の確かめの部品（Component）は、照会の継手（A-09）と印の照合の部品(Primitive)（A-10）を束ね、check({ domain, purpose, key_id }) を 1 回で行う部品です。まず DNS TXT（`_kokoroamu.<domain>`）を照会し、目的と鍵 ID がちょうど一致する印があり期限内なら MATCHED（dns_txt）。そうでなければ HTTPS の well-known を照会し、一致すれば MATCHED（https_well_known）。両方が答えて一致が無ければ NOT_FOUND、決められないときは UNAVAILABLE です（fail-closed）。MATCHED は管理権の判断ではありません。",
        "problem": "組織の確認の画面の裏で、照会の順と断る条件を決めておくための部品です。ほかの目的・ほかの鍵の印で通してしまうこと、別のドメイン・目的・鍵・確かめ方の答えを取り違えること、決められないのに「無い」と決めてしまうこと、期限の判断を自分で作ってしまうことを防ぎます。",
        "use_when": [
          "組織のドメインに、決めた目的と鍵 ID（16 桁の小文字 hex）の印があるかを、DNS を先に、次に HTTPS の順で 1 回の呼び出しで確かめたいとき。",
          "照会してよい目的を設定の表（allowed_purposes、1〜64 個。今は `seat8`）に限り、表に無い目的では照会もしないようにしたいとき。",
          "結果を MATCHED・NOT_FOUND・UNAVAILABLE の 3 つで受け取り、「両方が答えて一致が無い」と「決められなかった（後でやり直せる）」を分けて扱いたいとき。",
          "印の期限の判断を、A-10 の isClaimCurrent に、照会の受け取りの後に読んだ時刻で任せたいとき（記録の有効日数は valid_days、1〜90）。",
          "MATCHED のとき、決めた照会の受け取りの request_id（receipt_ref）と照会を送る前の時刻（observed_at）を、後の記録に添えたいとき。"
        ],
        "do_not_use_when": [
          "MATCHED を、ドメインの管理権の判断・署名してよいことの根拠として使いたいとき。MATCHED は「この目的とこの鍵 ID の印が、照会の時点でこのドメインの DNS か well-known にあった」という事実だけです。",
          "同じ端末・ドメインへの照会の回数の上限まで任せたいとき。回数の上限は呼ぶ側の規則と人の役目です。",
          "答えない照会の port を直接つなぐとき。この部品は待ち時間を切らないので、check が終わりません（本物の A-09 のクライアントは limits.timeout_ms に 1 秒を足した時間で UNKNOWN を返します）。",
          "receipt_ref が指す受け取りを、再起動の後も引けることが要るとき。A-09 のクライアントの記録はプロセスの中だけで、再起動で消えます。呼ぶ側の記録に頼ります。",
          "attestation の属性まで作らせたいとき。A-10 の claimToAttestationAttributes は使いません。属性を作るのは呼ぶ側です。",
          "本物の DNS・HTTPS で確かめた部品が要るとき。実の照会は 0 回です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違うとき INVALID_OPTIONS で止めます。check の入力がオブジェクトでない・読めないとき INVALID_INPUT、domain が契約の DomainName（242 文字まで）でないとき INVALID_DOMAIN、purpose が表に無いとき PURPOSE_NOT_ALLOWED、key_id が 16 桁の小文字 hex でないとき INVALID_KEY_ID で断ります。断るときは照会 0 回・時計を読まず、state は null です。",
          "時計が投げた・数でない・2000〜9998 年の外のとき、CLOCK_FAILED でそこで止めて UNAVAILABLE にします（それ以上照会しません）。",
          "A-09 の答えが REFUSED のとき LOOKUP_REFUSED、UNKNOWN のとき LOOKUP_UNKNOWN、RECEIPT_REJECTED か HALTED のとき LOOKUP_HALTED、DONE 以外（ALREADY_DONE を含む）のとき LOOKUP_NOT_DONE、下位の部品が例外を投げた・reject したとき PORT_FAILED で、MATCHED にしません。",
          "DONE でも、結果が聞いたドメイン・目的・鍵 ID・確かめ方を写していない・approval が false でない・result が無いとき、RESULT_MISMATCH で使いません。",
          "照会が答えられなかったとき（TIMEOUT・FAILED・TLS_FAILED・REDIRECT_REFUSED・知らない状態）LOOKUP_FAILED にします。FAILED は理由（PRIVATE_ADDRESS・DNSSEC_BOGUS・CONTENT_TYPE_REFUSED など）によらず「答えられない」です。",
          "A-10 の parseMarks が印を読めないとき MARKS_UNREADABLE、checkClaim が真偽値の matched を返さない・聞いたものと違う claim を返すとき CLAIM_UNCHECKED、isClaimCurrent が current: true を返さないとき（NOT_YET_OBSERVED・EXPIRED など）CLAIM_NOT_CURRENT で、MATCHED にしません。",
          "設定と目的の表は作るときに写し、A-10 へ渡す表も写します。後から options や表を書き換えられても、次の check は広がりません。"
        ],
        "what_it_does_not_do": [
          "組織のドメインの管理権の確かめの部品は継手ではありません。自分の Request/Receipt は無く、外への照会は、束ねた A-09 の継手が自分の契約（org-domain-control/v1）で行います。この部品自身は外へ触れず、照会も読むだけです（side effect NONE）。",
          "照会の検め（受け取りの照合・転送・TLS・DNSSEC・つないだアドレス・content type）は A-09 の側に、印の読み取り・照合・期限は A-10 の側に残ります。印がそのドメインから来たことは A-09 と port の役目で、この部品は A-09 の DONE を信じます。",
          "MATCHED は、管理権の判断でも、権限でも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "受け取りの queried_at（port の申告）と時計の食い違いは照らしません。照会の待ち時間も、この部品は切りません。",
          "本物の DNS・HTTPS には一度も照会していません。試験とファズは代役で、結線の検めは本物の A-09・A-10 のコードと、A-09 の試験用の代役の port を相手に確かめました。",
          "目的の表の置き場所と、今の目的のほかの名前は、この部品では決めていません（契約の残り）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の DNS サービス・HTTP クライアントの使い方の雛形ではありません。"
        ],
        "integration": "src/org-domain-check.js の createOrgDomainCheck(options) で作り、{ ok, errors, instance } を返します。options は allowed_purposes・contract_schema_digest・resolver_profile_ref・limits・valid_days・request_prefix・clock・ports です。check は { ok, errors, state, method, observed_at, receipt_ref, approval: false, … } を返します。port は domainControl.submit（A-09 の継手のクライアント）と A-10 の parseMarks・checkClaim・isClaimCurrent。資格情報は不要です。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-01",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "署名（KMS/HSM） — KMS署名継手（Tsugite）",
        "summary": "KMS署名継手（Tsugite）は、裏方の署名 API（Cloud KMS/HSM）の前に立ち、採択済みの schema・context と、RFC 8785 で正準化した payload だけを受け付け、sequence と previous_hash の連鎖を検めてから署名を頼み、返った署名を pin した公開鍵（PEM）で検証してから返す継手（つぎて）です。一つでも合わなければ KMS を呼ばず拒否します（fail-closed）。",
        "problem": "成果物や記録に電子署名するとき、任意のバイト列に署名してしまうこと、採択していない形式や context に署名してしまうこと、署名の順番（sequence）や前とのつながり（previous_hash）が崩れたまま署名してしまうことを防ぎます。署名 API を、任意の入力を受けない fail-closed の部品にするための検めです。",
        "use_when": [
          "成果物・記録への電子署名を、採択した schema・context と正準化した payload に限って行いたいとき。",
          "署名を context ごとの追記専用（append-only）の鎖（sequence の単調増加と previous_hash の連鎖）に並べたいとき。",
          "返ってきた署名を、公開鍵を都度取りに行かず、pin した PEM でその場で検証してから使いたいとき。",
          "Cloud KMS の AsymmetricSign（EC_SIGN_P256_SHA256）を、JSON 鍵を置かない認証（attached SA / ADC）と鍵単位の最小権限で使う構成にしたいとき。"
        ],
        "do_not_use_when": [
          "任意のバイト列やファイルにそのまま署名したいとき。canonical_payload は RFC 8785 で正準化した文字列に限り、任意の payload は拒否します。",
          "正準化もこの継手にまかせたいとき。RFC 8785 の正準化は呼び出し側が行います。",
          "実際の Cloud KMS・Firestore につないだ状態で試験済みの部品がほしいとき。この部品の試験は、ローカルの P-256 鍵と in-memory の store で行っており、本番の Cloud KMS・Firestore への接続は確かめていません。",
          "署名を、承認や決裁として扱いたいとき。この継手は検めて署名を継ぐだけで、権限は与えません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは融合核（Kernel）が担います。"
        ],
        "what_it_checks": [
          "schema と context を検めます。採択していない schema・context、誤った context（wrong-context）は、KMS を呼ばず 422 で拒否します。",
          "sequence を検めます。巻き戻し・重複・整数でない sequence は、KMS を呼ばず拒否します。",
          "previous_hash を検めます。直前の payload の SHA-256（または GENESIS）とつながらないもの、genesis の規則違反（approval の genesis が定まっていない場合を含む）は拒否します。",
          "payload を検めます。canonical_payload が無いもの、任意の payload は拒否します。",
          "payload の中身を依頼と照らします。payload の context・schema_version が依頼と同じときだけ署名し、違う payload には署名しません。",
          "知らない鍵の指定は 400 で拒否します。鍵は erabazu_publisher か approval_checkpoint です。",
          "署名を検証します。KMS が返した署名を pin した公開鍵（PEM）で検証できなければ、署名を返さず 500 とします。"
        ],
        "what_it_does_not_do": [
          "この部品の試験（単体試験とファズ）では、本番の Cloud KMS・Firestore には接続していません。試験はローカルの P-256 鍵と in-memory の store で行いました。別に、2026-08-15 に本番の ERABAZU HSM 鍵で synthetic の context（erabazu.synthetic-test）に署名し、pin した PEM で検証できた記録があります。",
          "canonical_payload の正準化（RFC 8785）はしません。呼び出し側が行います。",
          "任意の入力には署名しません。context・schema・正準化した payload に限ります。",
          "GET /latest が返す次の sequence・previous_hash は目安（ヒント）です。署名の可否は POST /sign の検めで決まります。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "署名 API の使い方の雛形ではありません。"
        ],
        "integration": "POST /sign に { key, context, schema_version, sequence, previous_hash, canonical_payload } を渡すと、200（signature_der_base64（ECDSA DER）・payload_sha256・verified_with_pinned_pem: true など）、400（知らない鍵）、422（fail-closed、reasons 付き）、500（pin した鍵で検証できない。署名なし）のどれかを返します。入口は src/server.js（handleSign）、設定は app_kms_config.json。利用側が用意するもの: signer の port（本番は Cloud KMS）と store の port（getLatest・commit。本番は Firestore）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-02",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "署名検証（pinned） — 署名検証（pinned）の部品（Primitive）",
        "summary": "署名検証（pinned）の部品（Primitive）は、bytes の上の署名を、使う側が固定した（pinned）公開鍵で手元で検証する単独の部品です。alg は Ed25519 と ES256 だけで、鍵は pinned（key_id → { alg, public_key_jwk }）からだけ取り、要求が鍵を持ち込むことはできません。verify は例外を投げずに { ok, errors } を返し、受け取れない値は決まったコードで止めます（fail-closed）。",
        "problem": "受領物が「本物か」を誰も確かめない、という困りごとに対して、出所・完全性を手元で検証します。そのとき、要求が持ち込んだ鍵で照合してしまうこと、alg を取り違えること、秘密鍵の JWK を pinned に入れた設定の誤り、Node.js の取り込みが受け取ってしまう正準でない符号化を、先に止めます。",
        "use_when": [
          "受け取った bytes の署名を、使う側が決めて固定した（pinned）公開鍵だけで検証したいとき。",
          "alg が Ed25519（RFC 8032 の R||S）か ES256（P-256 の ECDSA・SHA-256、署名は IEEE P1363 の r||s の 64 bytes）の署名を検証したいとき。",
          "例外を投げない検証が要るとき。verify は { ok, errors } を返し、例外を投げず、入力を書き換えません。",
          "JSON の値の署名を検証したいとき。I-08 の部品で正準の bytes にしてから、この部品に渡します。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
        ],
        "do_not_use_when": [
          "署名者が誰か（身元）を確かめたいとき。署名が正しいことは、pinned の鍵の持ち主がこの bytes に署名したことだけを示します。",
          "どの鍵を pin するか・鍵の更新と失効・key_id の割り当てを任せたいとき。これらはこの部品の外（使う側・trust の管理）で決めます。",
          "署名の bytes を一意の識別子に使いたいとき。ES256 の署名は malleable（(r, n - s) も正しい）で、low-S を強制しません。",
          "Ed25519・ES256 のほかの alg（EdDSA・ES384・RS256・none など）や、DER の形の ES256 署名、事前にハッシュした値を検証したいとき。",
          "JWS の全体（ヘッダー・payload の組み立て・base64url の区切り）を扱いたいとき。この部品は署名の bytes の検証だけを受け持ちます。",
          "Ed25519 の厳密さ（小さい位数の点・正準でない点・RFC 8032 と ZIP-215 などの違い）を自分の決まりで揃えたいとき。OpenSSL の実装に従い、小さい位数の点の公開鍵も断りません。",
          "Node.js v24.21.0 のほかの版（OpenSSL の版の違いを含む）で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "要求が素のオブジェクトでないとき NOT_A_REQUEST、alg・key_id・signature・bytes のほかのフィールド（鍵を持ち込む public_key_jwk・seed など）を持つとき UNKNOWN_FIELD で止めます。",
          "alg が \"Ed25519\" でも \"ES256\" でもないとき（大文字小文字の違い・none を含む）UNSUPPORTED_ALGORITHM、key_id が 1〜256 文字の文字列でないとき INVALID_KEY_ID で止めます。",
          "signature が正準の base64url でないとき（パディング・標準の base64 の文字・空白・余りの bit）MALFORMED_SIGNATURE、読むと 64 bytes でないとき（DER を含む）INVALID_SIGNATURE_LENGTH で止めます。",
          "bytes が Uint8Array でないとき（文字列・ほかの型付き配列・DataView・Proxy）、NOT_BYTES で止めます。",
          "pinned が素のオブジェクトでないとき INVALID_PINNED_SET、pinned のどこか（列挙しない項目を含む）に秘密のメンバーを持つ JWK があるとき、どの key_id でも PRIVATE_KEY_REFUSED で止めます。",
          "key_id が pinned の自分のメンバーに無いとき（__proto__ などのプロトタイプの名前を含む）UNKNOWN_KEY、pinned の項目・JWK の形が違うか曲線の上に無い点のとき INVALID_PINNED_KEY で止めます。",
          "要求の alg と pinned の alg が違うとき ALG_MISMATCH、bytes・署名・鍵のどれかが違うとき（Ed25519 の S + L を含む）SIGNATURE_INVALID を返します。",
          "入力を調べること自体が例外になるとき（getter・Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。入力は一度だけ読み、取り込みにはこの関数が作り直した JWK を渡します。"
        ],
        "what_it_does_not_do": [
          "鍵の配布・pin の決定・更新と失効（trust の管理）、JWS・manifest の組み立ては含みません。",
          "署名の作成はしません。秘密鍵を扱うのは KMS などの継手で、この部品は秘密のメンバーを持つ JWK を受け取りません。",
          "楕円曲線の計算は Node.js の crypto（OpenSSL）に任せ、独立には確かめていません。ファズのオラクルも同じ crypto を別の取り込み方で呼ぶだけです。",
          "RFC 8032 の例は 1 つ（例 1）だけで確かめ、ES256 は公開の既知の答えの代わりに、実行時に作った鍵の署名で確かめました。",
          "署名が正しいことは、署名者の身元・法的な受け入れ・権限・承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "ほかの部品・継手の中の署名の検証との互換は確かめていません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の署名のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/signature-verify.js の verify(request, pinned) を呼びます（投げません）。受け取るのは、alg・key_id・signature・bytes（Uint8Array）だけの素のオブジェクト request と、key_id → { alg, public_key_jwk }（公開の JWK だけ）の素のオブジェクト pinned です。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code }] }（code は error_taxonomy）。鍵は pinned で受け取り、取りに行きません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto と util だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-03",
        "category": "B/暗号・信頼",
        "kind": "Component",
        "title": "保管時暗号（envelope） — 保管時暗号（envelope）の部品（Component）",
        "summary": "保管時暗号（envelope）の部品（Component）は、データを封筒暗号にするために、KMS で DEK を作る・包んだ鍵をほどく継手（kms-envelope）と、手元で AES-256-GCM の封筒を作る・開く部品(Primitive)（envelope-encrypt）を束ねる部品です。KMS から受け取るのは handle と包んだ鍵の参照だけで、DEK は port の中の withDataKey から、同じプロセスの中で手元の seal・open にだけ渡ります。封筒の key_ref・包んだ鍵の参照・KMS の鍵が合わなければ KMS に送らず、暗号化の文脈が違えば開きません（fail-closed）。",
        "problem": "「暗号化されているはず」の裏で、平文の DEK が部品の結果や状態に残ること、包んだ鍵の参照や KMS の鍵の合わない封筒を KMS に送ってしまうこと、暗号化の文脈が違う封筒・改ざんされた封筒・違う aad の封筒を開いてしまうことを防ぎます。",
        "use_when": [
          "データ（plaintext、Uint8Array）を、KMS で作らせた DEK で封筒にしたいとき（encrypt）。ports.kms に kms-envelope の継手のクライアント、ports.seal・open に envelope-encrypt の Primitive を注入します。",
          "平文の DEK を、KMS の port の外に出さずに使いたいとき。port の中の keyAccess.withDataKey(handle, fn) が DEK を fn に渡し、この部品は fn の外へ DEK を出しません。",
          "暗号化の文脈を object_ref と context_digest（64 桁の 16 進）に結び付け、違う文脈の封筒を開かないようにしたいとき。",
          "封筒・包んだ鍵（wrapped_key）・暗号化の文脈の digest をまとめて保管し、decrypt で開きたいとき。開く前に、封筒の key_ref と包んだ鍵の参照、KMS の鍵が合うかを確かめます。"
        ],
        "do_not_use_when": [
          "暗号化できた・開けたことを、データを使ってよいこと・権限・承認として使いたいとき。暗号化はアクセス制御ではなく、どの結果にも approval: false・authorization: NOT_DERIVED が付きます。",
          "包んだ鍵・封筒の保管や鍵の回転も任せたいとき。保管は呼び出し側、鍵の回転は B-05 の継手の役目です。",
          "DEK がメモリーから確実に消えることを当てにしたいとき。DEK を port の中に閉じ込めること・ゼロで消すことは port の役目で、確かめていません。",
          "同じ入力から同じ封筒を作りたいとき。IV は Primitive が乱数で作るので、封筒は毎回違います。",
          "16 MiB を超える平文、64 KiB を超える aad を扱うとき。上限は Primitive のままです。",
          "本物の KMS で確かめた部品が要るとき。本物の KMS には一度も接続していません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき（bytes でない・context_digest が 64 桁の 16 進でない・wrapped_key の形など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "封筒の key_ref と包んだ鍵の参照、または包んだ鍵の KMS の鍵と自分の kms_key_ref が合わないとき、KMS に送らずに WRAPPED_KEY_MISMATCH で止めます。",
          "暗号化の文脈が違うとき、KMS が見つければ CONTEXT_MISMATCH、継手が自分の記録から見つければ送らずに KMS_REFUSED で、開きません。",
          "封筒が改ざんされた・aad が違うなど Primitive の open が ok: true を返さないとき OPEN_REFUSED（認証の失敗を含む）、seal が ok: true を返さないとき SEAL_REFUSED で止めます。",
          "KMS の継手の結果が分からないとき KMS_UNKNOWN、送らなかったとき KMS_REFUSED、receipt を照合できず停止した・GENERATED に包んだ鍵の参照や版が無いとき KMS_HALTED、DENIED・KEY_DISABLED・FAILED など望んだ状態でないとき KMS_DENIED で止めます。",
          "handle が無い・継手が使えると true ちょうどで答えないとき HANDLE_NOT_USABLE、withDataKey が投げた・結果を返さない・32 bytes の DEK を渡さないとき KEY_ACCESS_FAILED で止めます。",
          "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。",
          "開いた平文がその封筒を作った平文と同じであること、何かが違うのに開いた回が 0 であることを、別に書いた手本と突き合わせるファズで毎回確かめました（不一致と fail-open は 0）。"
        ],
        "what_it_does_not_do": [
          "保管時暗号（envelope）の部品は継手ではありません。自分の Request/Receipt は無く、KMS とのやり取りは束ねた kms-envelope の継手が自分の契約（kms-envelope/v1）で行います。その要求はどれも intended_side_effect NONE で、この部品自身は外へ触れません。",
          "束ねた部品の意味（包めた・開けた ≠ 使ってよい）は、それぞれの部品の側に残ります。",
          "暗号化できた・開けたことは、承認でも権限でもありません（approval: false・authorization: NOT_DERIVED・plaintext_key_material: NOT_TRANSPORTED、derives_authority: false）。",
          "DEK も handle も持ち続けず、結果に鍵の中身は入りません。状態は通し番号だけで、再起動で消えます。",
          "包んだ鍵・封筒の保管、鍵の回転（B-05）、データを使ってよいかの判断はしません。DEK を port の中に閉じ込めること・期限やプロセスの終わりにゼロで消すことは port の役目で、確かめていません。",
          "本物の KMS には一度も接続していません。単体試験とファズは代役で、本物の 2 部品のコードとの結線は、DEK を中に持つ port の代役を相手に確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の KMS の使い方の雛形ではありません。"
        ],
        "integration": "src/envelope-encryption.js の createEnvelopeEncryption(options) で作ります。受け取るのは kms_key_ref・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。encrypt は envelope・wrapped_key・encryption_context_digest を、decrypt は plaintext を返し、どの結果にも approval: false が付きます。利用側が用意する port は kms（kms-envelope の継手のクライアント）、keyAccess.withDataKey、seal・open（envelope-encrypt）です。副作用は NONE で、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-04",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "伝送時暗号（TLS/mTLS） — 伝送時暗号（TLS/mTLS）の構成検証の部品（Primitive）",
        "summary": "伝送時暗号（TLS/mTLS）の構成検証の部品（Primitive）は、TLS の構成のオブジェクトを、固定した profile（erabazu.primitive/tls-config/modern-2026/v1）の規則 R1〜R10 に照らして検める単独の部品です。TLS そのものはしません（接続しない・証明書を読まない）。規則に合わない値は、決まったコードと位置で止め、違反はすべて決まった順で返します（fail-closed）。",
        "problem": "「鍵マークだけ見て終わり」という困りごとに対して、TLS の構成そのものを版つきの規則で検めます。TLS 1.0/1.1・SSL、弱い cipher、相手の証明書やホスト名を確かめない設定、証明書や鍵の中身（PEM・base64 の塊）の混入を、黙って通しません。",
        "use_when": [
          "TLS の構成（min_version・max_version・ciphers_tls12・reject_unauthorized・verify_hostname・ca_refs・client_auth・alpn）を、使う前に規則 R1〜R10 で検めたいとき。",
          "TLS 1.2 を許す構成で、cipher を ECDHE の AEAD 6 つの名前の一覧（allowlist）だけに限りたいとき。",
          "mTLS のサーバー側の設定として、相手の証明書を求めるとき（client_auth.required が true）に、信頼の起点の参照（client_auth.ca_refs）が揃っているかを検めたいとき。",
          "違反を 1 つずつでなく、すべて決まった順（R1 → … → R10）で、コードと位置（JSON Pointer）付きで受け取りたいとき。",
          "時計・乱数・環境の locale・外の状態に左右されず、外の境界を持たない処理として手元で呼びたいとき。verify は例外を投げず、入力を書き換えません。"
        ],
        "do_not_use_when": [
          "経路の暗号化・相互認証そのもの（TLS の接続）をしてほしいとき。この部品は構成を検めるだけで、接続しません。",
          "証明書そのもの（期限・鍵の長さ・署名のアルゴリズム・鎖）、失効（CRL・OCSP）、相手の名前の一致を確かめたいとき。",
          "証明書の発行・更新や、構成をサーバー・ロードバランサー・クラウドの設定に当てはめることを求めるとき。発行・更新は B-09、当てはめは継手の側に残ります。",
          "OpenSSL の cipher 文字列（: で区切る・! で除く・HIGH などのまとまり）をそのまま検めたいとき。受け取るのは cipher の名前の配列だけです。",
          "TLS 1.3 の cipher suite・鍵交換のグループ（曲線）・署名のアルゴリズム・セッションの再開・0-RTT を決めたいとき。profile では決めていません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "構成が素のオブジェクトでないとき NOT_A_CONFIG、決めていないキー（__proto__ を含む）があるとき UNKNOWN_KEY、必須のキーが無いとき（値が undefined を含む）MISSING_KEY で止めます。",
          "SSLv2・SSLv3・TLSv1・TLSv1.1 のとき VERSION_TOO_OLD、ほかの正しくない版の値のとき INVALID_VERSION、max_version が min_version より下のとき VERSION_RANGE_INVERTED で止めます。",
          "TLS 1.2 を許すのに ciphers_tls12 が無いとき CIPHERS_REQUIRED、空の配列や cipher 文字列のとき INVALID_CIPHER_LIST で止めます。",
          "許した 6 つのほかの cipher（CBC・RC4・3DES・NULL・anon・export・静的 RSA・DHE・TLS 1.3 の名前・大文字小文字や空白の違い・配列の穴）のとき、CIPHER_NOT_ALLOWED で止めます。",
          "reject_unauthorized が true でないとき REJECT_UNAUTHORIZED_REQUIRED、verify_hostname が true でないとき VERIFY_HOSTNAME_REQUIRED で止めます（\"true\"・1 も違反です）。",
          "ca_refs が空のとき INVALID_CA_REFS、参照に PEM の印があるとき CA_REF_LOOKS_LIKE_PEM、128 文字超えや + = 改行を含むとき CA_REF_LOOKS_LIKE_BLOB、決めた文字の並びでないとき INVALID_CA_REF で止めます。",
          "client_auth の形が違うとき INVALID_CLIENT_AUTH、required が true なのに client_auth.ca_refs が無いか空のとき CLIENT_CA_REFS_REQUIRED、alpn が空のとき INVALID_ALPN、id が表示できる ASCII でないとき INVALID_ALPN_ID で止めます。",
          "値を調べること自体が例外になるとき（Proxy の罠・例外を投げる getter）、INSPECTION_FAILED を返し、例外を投げません。getter は値ごとに 1 回だけ読み、その写しで検めます。"
        ],
        "what_it_does_not_do": [
          "経路の暗号化・相互認証を名前に持つ項目ですが、この部品は TLS の構成を検めるだけで、TLS の接続・証明書の読み込みは含みません。構成が規則に合うことは、接続を確かめたことではありません。",
          "証明書そのもの（期限・鍵の長さ・署名のアルゴリズム・鎖）、失効（CRL・OCSP）、相手の名前の一致は確かめません。",
          "実行環境（Node.js・OpenSSL・ロードバランサーなど）がこの構成を本当に守るか、名前の書き方を受け取るかは確かめていません（tls に渡していません）。",
          "ca_refs の参照が何を指すか・解決できるかは確かめません。見た目の検めなので、128 文字以内で決めた文字の並びに合う文字列は参照として受け取ります。",
          "許す cipher の一覧は 2026-09-30 の判断です。後で弱くなった場合は profile の版を上げて直します。",
          "構成が規則に合うことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のサーバーやクラウドの TLS 設定の雛形ではありません。"
        ],
        "integration": "src/tls-config.js の verify(config) を呼びます（投げません）。受け取るのは、min_version・max_version・ciphers_tls12・reject_unauthorized・verify_hostname・ca_refs・client_auth・alpn の 8 つのキーだけの素のオブジェクトです。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code, path }, ...] }（code は error_taxonomy、path は JSON Pointer。違反は規則の順にすべて並ぶ）。実際の設定の文字列にするのは使う側です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-05",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "鍵管理・回転 — 鍵管理・回転の継手（Tsugite）",
        "summary": "鍵管理・回転の継手（Tsugite）は、外の KMS・HSM へ鍵の操作（鍵を作る・新しい版を作る＝回転・primary を替える・版を無効にする・有効に戻す・破棄を予約する・予約を取り消す・様子を見る）の要求（KeyLifecycleRequest）を検めてから運び、返った receipt（KeyLifecycleReceipt）を契約どおりに照合する継手（つぎて）です。鍵の中身は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "鍵の版の無効化や破棄の予約を、人・組織の権限の証拠なしに送ってしまうこと、ENABLED でない版を primary にしてしまうこと、短すぎる猶予で破棄を予約してしまうこと、そして鍵の操作が通ったことを鍵を使う許可と取り違えることを防ぎます。",
        "use_when": [
          "外の KMS・HSM で鍵を作り、新しい版を作って（回転）primary を替える操作を、契約どおりに検めてから運びたいとき。",
          "版の無効化と破棄の予約を、その鍵・版・op に結ばれた人・組織の権限の証拠を受け取ったときだけ送りたいとき。証拠は 1 回の変更で使い切ります。",
          "破棄を、すぐにではなく猶予（1 日以上、設定の下限以上）を置いた予約としてだけ行い、CANCEL_DESTROY で DISABLED に戻せるようにしたいとき。",
          "primary にする版を、この継手が ENABLED と照合した版だけに限りたいとき。"
        ],
        "do_not_use_when": [
          "鍵をすぐに破棄したいとき。すぐに破棄する op は契約にありません。",
          "回転の方針（いつ回すか・何世代残すか）を検めたいとき。これは手元の Primitive の役目で、この継手には入っていません。rotation_policy_ref は運ぶだけです。",
          "鍵を使って暗号化・署名したいとき。暗号化は KMS envelope の継手（B-03 では部品(Component) がこれを束ねます）、署名は B-01 KMS 署名の継手の役目です。",
          "鍵の中身（公開鍵を含む）を受け渡したいとき。この継手は鍵の中身を運ばず、公開鍵も参照だけです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える KMS・HSM への接続がほしいとき。実の KMS・HSM には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "設定を検めます。設定が無い、一覧に無い KMS・鍵の仕様、下限（min_destroy_window_seconds）の無い破棄の予約、下限や 1 日より短い猶予は送りません。",
          "権限の証拠を検めます。証拠の欄が無い、証拠を受け取っていない、出どころが違う（HUMAN・ORGANIZATION でないものを含む）、ほかの鍵・版・op の subject digest、使い切った証拠では、無効化・破棄の予約を送りません。",
          "primary の替え先を検めます。知らない版・ENABLED でない版、食い違う版の記録があれば SET_PRIMARY_VERSION を送りません。",
          "参照と要求の形を検めます。ドライブ・..・制御文字・行区切り・空白や PEM を含む参照、Ref でない request_id、契約に無い op（DESTROY を含む）、op が使わない欄、op ごとの必須の欄の欠けは送りません。",
          "receipt を照合します。request_digest の偽り、別の request_id・KMS・鍵・版、op の食い違い、表の外の状態、効果を増やす・不明な効果、断ったのに効果がある receipt は受理せず、停止します（HALTED）。",
          "鍵の中身・公開鍵そのものを載せた receipt、鍵を使う許可（authorization）を導いた receipt は受理しません。",
          "破棄の時刻を検めます。猶予より手前・過去の破棄の時刻、時刻の無い予約、DESTROY_SCHEDULED でないのに時刻がある receipt、DISABLED に戻らない CANCEL_DESTROY は受理しません。",
          "結果の分からない送信は同じ要求の再送（lookup）で解き、解けるまでほかは送りません。同じ request_id で別の中身の要求は送りません。"
        ],
        "what_it_does_not_do": [
          "権限の証拠を作りません。版の無効化と破棄の予約には、部品の外の人・組織の証拠が入力として要ります（HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_KEY_DISABLE_AND_DESTROY）。証拠の中身（誰が出したか・期限・本物か）も読みません。",
          "鍵の操作が通っても、鍵を使う許可でも業務の承認でもありません（authorization は NOT_DERIVED、approval: false）。",
          "鍵の中身を保管しません。この継手は鍵の操作の要求を運ぶ継手で、鍵の中身は運ばず・持たず、回転の方針を決める機能も含みません。",
          "KMS 側の破棄の猶予が要求より短いか、予約どおりに破棄されたか、取り消しが効いたかは、KMS の答え（DESCRIBE）でしか知りません。",
          "参照の欄が鍵の中身でないことは、形でしか確かめられません。短い符号化された鍵の文字列を参照と見分けることはできません。",
          "版の状態の記録はこの継手の中だけにあり、再起動で消えます。ほかの者が KMS で版の状態を変えると記録は古くなります。資格情報は port 側が持ちます（HELD_BY_PORT）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。",
          "特定のクラウド KMS・HSM の鍵管理 API の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/key-lifecycle/v1 でつなぎます。createKeyLifecycleClient({ cfg, port }) の submit(request) に KeyLifecycleRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。無効化・破棄の予約の前に recordAuthorityEvidence で証拠を渡します。利用側が用意するもの: KMS・HSM への port（submit、任意で lookup）。副作用: 破棄の予約は EXTERNAL_IRREVERSIBLE、DESCRIBE は NONE、ほかは EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-06",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "信頼タイムスタンプ（RFC3161） — タイムスタンプ継手（Tsugite）",
        "summary": "タイムスタンプ継手（Tsugite）は、時刻認証局（TSA）から返った時刻トークンの欄を、こちらが頼んだ内容（ハッシュ・nonce）と pin した TSA・時刻のずれ・精度の上限に照らして検め、一つでも合わなければ信頼しない継手（つぎて）です（fail-closed）。RFC 3161 の DER の解析はせず、署名の検証は 6 つの欄の JSON に対して行います。",
        "problem": "契約や検査で「いつ存在したか」を示すとき、別のハッシュに差し替えられた時刻トークン、使い回し（リプレイ）されたトークン、pin していない TSA のトークン、ずれや精度が大きすぎる時刻を、そのまま信じてしまうことを防ぎます。返ってきた時刻を検めてから来歴へ継ぐための検めです。",
        "use_when": [
          "TSA から受け取った時刻トークンが、こちらの送ったハッシュ（message_imprint）と nonce に一致するかを必ず確かめたいとき。",
          "使う TSA を pin し、時刻のずれ（max_skew_seconds）と精度（max_accuracy_seconds）の上限を決めて検めたいとき。",
          "トークンの欄の署名を、pin した公開鍵（PEM）で検証したいとき（署名は DER 形式・sha256）。",
          "「いつ存在したか」を来歴の核（provenance-facts）へ継ぎたいとき。"
        ],
        "do_not_use_when": [
          "TSA が返す RFC 3161 の DER（ASN.1）形式のトークンを、そのまま渡して検めたいとき。この継手は DER を解析しません。トークンは 6 つの欄を取り出した形で渡す必要があり、署名の検証の対象も、その 6 つの欄の JSON です。",
          "TSA へのタイムスタンプの依頼や、TSA の証明書の取得・pin までまかせたいとき。TSA への通信は含みません（port は無し）。",
          "時刻トークンを発行したいとき。src/tsa.js の signToken は試験用の TSA の代役で、本物の TSA ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "imprint を検めます。トークンの message_imprint が、こちらの送ったハッシュと一致しなければ（差し替え）信頼しません。",
          "nonce を検めます。こちらの nonce がそのまま返っていなければ（リプレイ）信頼しません。",
          "TSA を検めます。pin していない TSA（tsa_id）のトークンは信頼しません。",
          "時刻を検めます。基準の時刻とのずれが上限を超えるもの、精度（accuracy_seconds）が上限を超えるもの、gen_time が整数でないものは信頼しません。",
          "ハッシュのアルゴリズムを検めます。採択していないアルゴリズムは信頼しません。",
          "署名を検めます。verifyToken は、渡された signature_valid が真でなければ信頼しません。verifyTokenSignature は、改ざんされたトークンや別の鍵の署名を、pin した公開鍵で検証できないものとして偽を返します。"
        ],
        "what_it_does_not_do": [
          "RFC 3161 の DER（ASN.1）形式の解析はしません。署名の対象は、トークンの 6 つの欄（message_imprint・hash_algo・tsa_id・nonce・gen_time・accuracy_seconds）の JSON です。",
          "実 TSA への通信と、TSA の証明書の取得・pin はしません。配備のときに決めます。",
          "verifyToken は署名そのものを検証しません。signature_valid（真偽値）を受け取って判定します。署名の検証は verifyTokenSignature で別に行います。",
          "試験では ECDSA P-256 の鍵と試験用の TSA の代役（signToken）を使っています。実 TSA は叩いていません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "TSA API の使い方の雛形ではありません。"
        ],
        "integration": "src/guards.js の verifyToken(cfg, request, token, refTime) に、request { hash, nonce, hash_algo }、token { message_imprint, hash_algo, tsa_id, nonce, gen_time, accuracy_seconds, signature_valid }、基準の時刻（秒）を渡すと { ok, errors } を返します。src/tsa.js の verifyTokenSignature(pinnedPubPem, fields, derSignature) は 6 つの欄の署名を検証し真偽値を返します。含まないもの: RFC 3161 の DER の解析、TSA への通信、TSA の証明書の取得と pin（配備のときに決めます）。port は無し。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-07",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "ハッシュ・内容アドレス — ハッシュ・内容アドレスの部品（Primitive）",
        "summary": "ハッシュ・内容アドレスの部品（Primitive）は、bytes の sha256、契約で使う型つきの digest（domain・algorithm・value・target）、内容アドレス（sha256:<hex>）を作り、照合する単独の部品です。受け取るのは bytes だけです。照合は形・アルゴリズム・値（定数時間の比較）・期待した domain と target を検め、{ ok, errors } を返して例外を投げません（fail-closed）。",
        "problem": "provenance・reconcile の土台として内容から一意の指紋を作り、照合するときに、bytes と文字列を取り違えること、形の違う digest や別のアルゴリズムを受け取ってしまうこと、domain・target を取り違えて照合してしまうことを防ぎます。",
        "use_when": [
          "bytes から sha256（小文字 64 桁の hex）や内容アドレス（sha256:<hex>）を作りたいとき。",
          "契約で使う型つきの digest（{ domain, algorithm: \"sha256\", value, target }）を作り、照合したいとき。domain と target は名札で、ハッシュには混ぜません。",
          "digest を bytes と照合するときに、期待した domain・target を渡して取り違えを止めたいとき。",
          "例外を投げない照合が要るとき。verifyDigest と parseContentAddress は { ok, errors } を返し、例外を投げません。",
          "JSON の値の digest を取りたいとき。I-08 の部品で正準の bytes にしてから、この部品に渡します。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。"
        ],
        "do_not_use_when": [
          "文字列をそのまま渡したいとき。この部品は文字列を受け取りません（NOT_BYTES）。文字列は、使う側が決まった文字コード（UTF-8 など）で bytes にしてから渡します。",
          "digest の一致で、出どころを証明したいとき。一致は出どころの証明ではありません。署名の検証は B-02 です。",
          "sha256 のほかのアルゴリズムが要るとき。",
          "bytes の大きさの上限が要るとき。上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "bytes（Uint8Array）でないとき（文字列・数・配列・ArrayBuffer・ほかの型付き配列）、NOT_BYTES で止めます。",
          "domain の形が違うとき INVALID_DOMAIN、target が空・文字列でない・1024 文字を超えるとき INVALID_TARGET で止めます。",
          "digest が 4 つのキーだけのオブジェクトでないとき（キーの余り・欠け・digest が無い）、NOT_A_DIGEST で止めます。",
          "algorithm が sha256 でないとき UNSUPPORTED_ALGORITHM、value が小文字 64 桁の hex でないとき（大文字・長さ違い）MALFORMED_VALUE で止めます。",
          "bytes の sha256 が value と違うとき、定数時間の比較（crypto.timingSafeEqual）で VALUE_MISMATCH を返します。",
          "期待した domain・target と違うとき DOMAIN_MISMATCH・TARGET_MISMATCH、expect がオブジェクトでないとき INVALID_EXPECTATION で止めます。",
          "内容アドレスが sha256:<小文字 64 桁の hex> でないとき、MALFORMED_CONTENT_ADDRESS で止めます。"
        ],
        "what_it_does_not_do": [
          "文字列の bytes 化・JSON の正準化はしません。文字列は使う側が bytes にし、JSON の値は I-08 の部品で正準の bytes にしてから渡します。",
          "digest の一致は出どころの証明ではありません。署名の検証（B-02）や鍵の管理は含みません。",
          "digest が一致したことは、中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "sha256 のほかのアルゴリズムは扱いません。",
          "継手の中にある digests.js の写し（19 個）を、まだこの部品に置き換えていません。bytes では写しの sha256hex と byte 単位で同じ値ですが、写しは文字列も受け取って UTF-8 としてハッシュし、この部品は bytes だけを受け取ります。置き換えは互換表を確かめた後の別の作業です。",
          "要求の正準化＋sha256（requestDigestValue）と、どのフィールドを digest に入れるかは決めません。使う側（各継手・契約）に残ります。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のハッシュのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/digest.js の verifyDigest(digest, bytes, expect) を呼びます（投げません）。受け取るのは、4 つのキーだけの digest、bytes（Uint8Array。Buffer を含む）、任意の expect（{ domain, target }）です。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code }] }（code は error_taxonomy）。sha256Hex・digest・contentAddress は受け取れない値で DigestError を投げ、parseContentAddress は投げません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-08",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "乱数・nonce（CSPRNG） — 乱数・nonce の部品（Primitive）",
        "summary": "乱数・nonce の部品（Primitive）は、OS の CSPRNG（Node.js の crypto.randomBytes）の上の薄い包みです。randomBytes(n)（1〜1024 bytes）、nonce（hex か base64url）、randomInt(maxExclusive)（1〜2^48 の範囲で一様）を作ります。受け持つのは引数の検め・出力の形・randomInt の棄却法だけで、乱数の質そのものは OS と Node.js の crypto に任せます。",
        "problem": "「弱いと全部崩れる裏方」である乱数の作り方を固定します。出どころを OS の CSPRNG に固定して差し替えられないようにし、使う側の seed を受け取らず、長さ・符号化・範囲を検め、randomInt は x mod m の偏りを作らない棄却法で一様にします。出どころが失敗したときは値を作らずに止めます（fail-closed）。",
        "use_when": [
          "OS の CSPRNG から、決まった長さ（1〜1024 bytes）の bytes を引きたいとき。",
          "nonce を小文字の hex か、パディングなしの base64url（RFC 4648 の URL と名前に安全な字母）の文字列で作りたいとき。",
          "0 以上 max_exclusive 未満の整数を、1〜2^48 の範囲（2^48 を含む）で偏りなく作りたいとき。",
          "使う側の seed が混じらないことを入口で守りたいとき。seed のフィールドは UNKNOWN_FIELD で断ります。",
          "例外を投げない呼び方が要るとき。generate(request) は { ok, errors, value } を返し、例外を投げません。"
        ],
        "do_not_use_when": [
          "乱数の質（予測できないこと・偏りのなさ）の確かめを求めるとき。この部品は確かめておらず、OS と Node.js の crypto.randomBytes に任せています。",
          "用途に足りる長さの判断を任せたいとき。長さは使う側が決め、1 byte の nonce も受け取ります。",
          "再現できる（決定的な）値が要るとき。値は呼ぶたびに OS の CSPRNG から引き、再現できません（設計どおり）。",
          "1 回に 1024 bytes を超える bytes や、2^48 を超える範囲の整数、bigint・文字列の数を扱いたいとき。",
          "fork したプロセス・仮想マシンの複製などで使うとき。そこでの OS の CSPRNG の振る舞いは確かめていません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "要求が素のオブジェクトでないとき（配列・Map・クラスのインスタンスなど）、NOT_A_REQUEST で止めます。",
          "kind が \"bytes\"・\"nonce\"・\"int\" のどれでもないとき（プロトタイプの名前を含む）UNKNOWN_KIND、決まったフィールドのほか（seed を含む）を持つとき UNKNOWN_FIELD で止めます。",
          "length・bytes が 1〜1024 の整数（number）でないとき（0・1024 超え・小数・文字列・bigint）、INVALID_LENGTH で止めます。",
          "encoding が \"hex\" でも \"base64url\" でもないとき INVALID_ENCODING、max_exclusive が 1〜2^48 の整数（number）でないとき INVALID_BOUND で止めます。",
          "乱数の出どころが投げた・長さの違う値や bytes でない値を返したとき、または randomInt で 64 回続けて捨てたとき、値を作らず ENTROPY_UNAVAILABLE で止めます。",
          "要求を調べること自体が例外になるとき（getter・Proxy の罠など）、generate は INSPECTION_FAILED を返します。フィールドは一度だけ読み、検めた値だけを使います。"
        ],
        "what_it_does_not_do": [
          "「予測不能な値の生成」を名前に持つ項目ですが、この部品は乱数の質（予測できないこと・偏りのなさ）を確かめていません。OS と Node.js の crypto.randomBytes に任せています。統計の試験は大きな偏りを見る煙の試験で、NIST SP 800-22 などの正式な統計試験ではありません。",
          "どの用途に何 bytes 使うか（nonce の一意性・トークンの強さ）は判断しません。使う側が決めます。",
          "出どころの失敗（ENTROPY_UNAVAILABLE）は、差し替えた出どころで確かめました。実際の OS の CSPRNG を失敗させては確かめていません。",
          "使う側の seed や、出どころを差し替える手段は受け取りません。公開の入口の module.exports は凍結してあり、内部の makeRandom は出していません。",
          "値ができたことは、使う側の目的に長さが足りることでも、乱数の質が正式な試験で確かめられたことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の乱数のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/random.js の generate(request) を呼びます（投げません）。受け取るのは { kind: \"bytes\", length }・{ kind: \"nonce\", bytes, encoding }・{ kind: \"int\", max_exclusive } のどれかです。返すのは { ok: true, errors: [], value }（Buffer・文字列・整数）か { ok: false, errors: [{ code }] }（code は error_taxonomy）。randomBytes・nonce・randomInt は受け取れないとき RandomError を投げます。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みの crypto と util だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-09",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "証明書発行・更新（ACME） — 証明書の発行・更新（ACME）の継手（Tsugite）",
        "summary": "証明書の発行・更新（ACME）の継手（Tsugite）は、ACME（RFC 8555）の認証局へ、注文（NEW_ORDER。更新は renewal_of_ref つきの新しい注文）・challenge の一覧・応答の準備ができたことの知らせ・注文の状態の確かめ・発行の依頼（FINALIZE）・証明書の取り出しの要求（AcmeRequest）を検めてから運び、返った receipt（AcmeReceipt）を照合する継手（つぎて）です。発行できたことを、名前の正当な所有や信頼として扱いません（fail-closed）。",
        "problem": "取り消せない発行（FINALIZE）を、注文と名前の合わない CSR や、人・組織の根拠なしに依頼してしまうこと、そして名前や公開鍵の違う証明書を受け取ったまま使ってしまうことを防ぎます。発行は Certificate Transparency に公に記録されるため、送る前と受け取った後の両方で検めます。",
        "use_when": [
          "TLS 証明書を ACME の認証局から取得し、更新（renewal_of_ref つきの新しい注文）するときの要求を、契約どおりに検めてから運びたいとき。",
          "発行の依頼を、この directory・注文・CSR・名前の主体（authority_subject_digest）に結ばれた人・組織の根拠を受け取ったときだけ送りたいとき。",
          "CSR の名前が注文の名前とちょうど同じで、CSR の中身と公開鍵の sha256 が要求の digest と同じことを確かめてから発行を依頼したいとき。",
          "取り出した証明書の名前と公開鍵を、連鎖を読み戻して注文・CSR と照合してから受け取りたいとき。"
        ],
        "do_not_use_when": [
          "証明書を失効させたいとき。失効（REVOKE）は v1 の契約に無く、別の判断が要ります。",
          "challenge の応答（HTTP のファイル・DNS の記録）を置きたいとき。それは別の port の役目で、この継手は challenge の参照と digest を返すだけです。",
          "更新の時期や期限切れを判断させたいとき。この継手は有効期間の形と前後を検めるだけで、その判断はしません。",
          "TLS の構成を検めたいとき。それは手元の Primitive（B-04）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える認証局への接続がほしいとき。実の認証局には一度も接続しておらず、port の実装・account 鍵の結び付け（JWS の署名）・nonce・設定の値は接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "設定を検めます。設定が無い・一覧が空、一覧に無い directory・account 鍵・名前、名前を覆わないワイルドカード、DNS でない・大文字・重なる・空・昇順でない名前の注文は送りません。",
          "発行の根拠を検めます。根拠を受け取っていない、出どころが違う（HUMAN・ORGANIZATION でないものを含む）、別の注文・CSR・名前・名前の順に結ばれた根拠では、FINALIZE を送りません。",
          "CSR を検めます。CSR を読めない、中身や公開鍵の sha256 が digest と違う、名前の欠け・余り・違い、注文の外の CN、SAN や extensionRequest が二つある CSR では、FINALIZE を送りません。",
          "注文の順番を検めます。知らない注文、directory や account 鍵の変わった注文、pending でない注文・challenge への知らせ、ready でない注文の FINALIZE、valid でない・この継手が FINALIZE していない注文の取り出しは送りません。",
          "参照と要求の形を検めます。..・ドライブ・制御文字・行区切りを含む参照、空白を含む account 鍵の参照、account 鍵の秘密鍵の欄、REVOKE、ほかの op の欄、Ref でない request_id は送りません。",
          "receipt を照合します。request_digest の偽り、別の op・directory・注文、名前の所有や信頼を導いた receipt、効果を増やす・小さく言う・不明な効果、注文や challenge の状態の後戻りは受理せず、停止します（HALTED）。",
          "証明書を照合します。leaf_names の欠け・余り・違い、CSR と違う公開鍵、連鎖を読み戻せない・sha256 が違う、有効期間の違い、名乗りと読み戻した leaf の食い違いがあれば、照合済みにしません。",
          "DER の読み取りで、最小でない長さ・高いタグ番号・後ろに余るバイトを受け付けません。"
        ],
        "what_it_does_not_do": [
          "発行の根拠を作りません。FINALIZE には、部品の外の人・組織の根拠が入力として要ります（HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_CERTIFICATE_ISSUANCE）。根拠の記録が本当に人・組織から来たかも確かめません。",
          "発行できた・取り出せたことは、名前の正当な所有でも信頼でもありません（name_ownership・trust_decision は NOT_DERIVED、approval: false）。ドメインを持っていることは確かめません。",
          "証明書の自動の取得・更新を自分で回しません。この継手は ACME の要求を運ぶ継手で、更新の時期の判断、challenge の応答を置くこと、失効（REVOKE）の機能は含みません。",
          "CSR の自己署名と、証明書の署名・連鎖・失効・信頼は検めません。leaf は連鎖の先頭の証明書とみなします。",
          "CSR と証明書は、要る DER だけを読む小さな読み取りで、試験は手元で組み立てた P-256 の CSR と証明書だけです。実の認証局が出した証明書では確かめていません。認証局が公開鍵の書き方を変えると、同じ鍵でも照合済みになりません。",
          "注文の状態と受け取った根拠はこの継手の中だけにあり、再起動で消えます。名前は小文字の ASCII だけを扱います。account 鍵などの資格情報は port 側が持ちます（HELD_BY_PORT）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。",
          "特定の認証局の ACME クライアントの雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/acme-certificate/v1 でつなぎます。createAcmeClient({ cfg, port }) の submit(request, csr) に AcmeRequest を渡すと（csr は FINALIZE のときだけ）、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。FINALIZE の前に recordAuthorityEvidence で根拠を渡します。利用側が用意するもの: 認証局への port（submit・readChain、任意で lookup）。副作用: FINALIZE は EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-11",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Merkle 木（根・包含証明） — Merkle 木（根・包含証明）の部品（Primitive）",
        "summary": "Merkle 木（根・包含証明）の部品（Primitive）は、bytes の葉の並びから、RFC 9162 の 2.1 節（Certificate Transparency v2）の Merkle Tree Hash（SHA-256）で根を作り、包含証明と一貫性証明を作って検める単独の部品です。どの操作も投げずに { ok, errors } を返し、形の悪い入力は決まったコードで止め、照合の不一致は verified: false（エラーではない）で返します。",
        "problem": "「束ねて署名した」の中身を、後から照らす土台になります。束ねた木の根に署名しておけば、ある葉が含まれていることを包含証明で部分的に確かめられ、追記した後の木が前の木を先頭に持つことを一貫性証明で確かめられます。葉と節の前置き（0x00・0x01）で、葉と節を取り違えた偽の葉を防ぎます。",
        "use_when": [
          "bytes の葉の並びから、RFC 9162 の木の根（小文字 64 桁の hex）を作りたいとき（merkleRoot）。",
          "ある葉が根に含まれることを示す包含証明（audit_path）を作り、検めたいとき（inclusionProof・verifyInclusion）。",
          "追記で大きくなる記録について、古い木が新しい木の先頭であることを示す一貫性証明を作り、検めたいとき（consistencyProof・verifyConsistency）。",
          "照合の不一致（証明・葉・番号・根の取り違え、足りない・余る証明）を、例外ではなく verified: false で受け取りたいとき。",
          "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。依存パッケージはありません。"
        ],
        "do_not_use_when": [
          "透明性ログが要るとき。この部品は木を保存・追記せず、署名つきの木の頭（STH）・ログの監視・gossip・ログの ID を扱いません。",
          "証明だけで大きいほうの木の大きさを決めたいとき。証明の通り道で形が同じ別の大きさでも true になることがあります。大きさと根は、署名された記録から取ってください。",
          "SHA-256 のほかのハッシュが要るとき、または 1,048,576 葉より大きな木・ログの証明を検めたいとき（BAD_SIZE）。",
          "old_size が 0 の一貫性や、大きさの同じ木（old_size = new_size）の一貫性を確かめたいとき。BAD_SIZE で止めます。同じ大きさなら、使う側が根を直接比べます。",
          "奇数のときに右端を複製する形（Bitcoin 型）の木が要るとき。この部品は複製せず、右端をそのまま上の段へ上げます。",
          "大きな木で証明を何度も作る使い方の速さが要るとき。証明を作るたびに渡された葉から木を作り直し（O(n)）、その速さは測っていません。葉の合計の bytes の上限も持たず、メモリーと時間は呼ぶ側の持ち分です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "leaves が配列でない、要素が bytes（Uint8Array）でない（穴・getter・ほかの型付き配列・ArrayBuffer・DataView・Proxy で包んだ bytes を含む）、引数がオブジェクトでないとき、NOT_BYTES_LIST で止めます。",
          "葉が 1,048,576 個を超えるときは要素を読む前に TOO_MANY_LEAVES、葉 1 つが 64 MiB を超えるときはハッシュの前に LEAF_TOO_LARGE で止めます。",
          "index・leaf_index が整数でない・負・木の大きさ以上のとき、INDEX_OUT_OF_RANGE で止めます。verifyInclusion でも、照合の失敗ではなくエラーとする厳しいほうの読みです。",
          "tree_size が 1〜1,048,576 の整数でないとき、old_size・new_size が 0 < old_size < new_size ≤ 1,048,576 の整数でないとき、BAD_SIZE で止めます。",
          "root・old_root・new_root と証明の要素が小文字 64 桁の hex の文字列でないとき BAD_HEX、audit_path・proof が配列でないとき BAD_PROOF_SHAPE で止めます。",
          "証明の要素の書き換え・抜け・追加・入れ替え、葉・番号・根の取り違えは通しません（verified: false）。作った証明は、同じ根と大きさで必ず true になります。",
          "止めるときは、決まった順で最初に見つけた 1 つのコードだけを返します。getter を呼ばず、調べると例外になる値（Proxy の罠など）でも投げません。"
        ],
        "what_it_does_not_do": [
          "署名はしません（根への署名は B-12）。時刻の証明もしません（B-06）。根が誰のものか・いつのものかは言いません。",
          "verified: true は、SHA-256 の衝突が無い限り、渡された葉が渡された根と大きさの木に含まれる（または古い木が新しい木の先頭である）と言えるというだけです。根が正しいこと、中身が正しいこと、受け入れてよいことは示しません。",
          "木・証明・記録を保存しません。呼び出しの間に状態を持たず、記録の保存と追記の管理は使う側（H-11 など）に残ります。",
          "比較は定数時間ではありません（根と証明は秘密ではない前提）。強さは SHA-256 の衝突・原像の困難さに頼ります。",
          "certificate-transparency の試験の 1〜8 葉の根は試験に書き写した値で、元のファイルを取り寄せて照合してはいません（定義から手で組んだ値と一致することで確かめました）。",
          "根・証明・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の透明性ログ（CT のログ・Trillian など）の雛形ではありません。"
        ],
        "integration": "src/merkle.js の merkleRoot・inclusionProof・verifyInclusion・consistencyProof・verifyConsistency を呼びます（どれも投げません）。引数は leaves・index・leaf・leaf_index・tree_size・audit_path・root・old_size・new_size・old_root・new_root・proof を持つオブジェクトで、葉は Uint8Array、根と証明の要素は小文字 64 桁の hex です。返すのは { ok: true, errors: [], ... } か { ok: false, errors: [{ code }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-12",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "手元の鍵での署名（Ed25519） — 手元の鍵での署名（Ed25519）の部品（Primitive）",
        "summary": "手元の鍵での署名（Ed25519）の部品（Primitive）は、呼ぶ側が持つ Ed25519 の秘密鍵（Node.js の KeyObject）で bytes に署名し、署名（詰め物なしの base64url）と、導いた公開鍵の SPKI DER の sha256（指紋）を返す単独の部品です。秘密鍵は KeyObject でだけ受け取り、書き出さず・保管せず・返しません。どの操作も投げずに { ok, errors } で返します（fail-closed）。",
        "problem": "記録への署名の裏で、鍵をどう扱うかは見えません。この部品は、bytes・PEM・JWK などで秘密鍵を持ち回る使い方を断り、秘密鍵を KeyObject でだけ受け取ります。形の違う鍵・署名は決まったコードで止め、止めるときは署名を返しません。",
        "use_when": [
          "呼ぶ側が持つ Ed25519 の秘密鍵で、bytes（16 MiB まで）に署名したいとき（sign）。何に署名するか（Merkle 木の根や正準の bytes など）は使う側が決めます。",
          "どの鍵で署名したかを、公開鍵の SPKI DER の sha256（public_key_spki_sha256）で示したいとき（sign・publicKeyFingerprint）。",
          "署名した直後に往復の確かめをしたいとき（verify。違えば verified: false で、エラーではない）。",
          "同じ鍵と同じ message から、いつも同じ署名が要るとき（RFC 8032 の Ed25519 の pure。時計・乱数を使わない）。",
          "KMS・HSM を使わず、手元の鍵で署名したいとき。この部品は KMS・HSM・鍵の保管庫とは話しません。"
        ],
        "do_not_use_when": [
          "使う側が pin した鍵で署名を検証したいとき。verify は往復の確かめで、その代わりではありません。pin した鍵での検証は B-02 です。",
          "鍵の保管・入れ替え・失効や、HSM・KMS での署名が要るとき。この部品は扱いません。",
          "秘密鍵を bytes・文字列・PEM・JWK・WebCrypto の CryptoKey で渡したいとき。KEY_NOT_KEYOBJECT で止めます。",
          "Ed25519 のほかの種類の鍵（EC・RSA・X25519・Ed448・共通鍵）で署名したいとき。KEY_TYPE_UNSUPPORTED で止めます。",
          "16 MiB を超える message に署名したいとき。MESSAGE_TOO_LARGE で止めます。",
          "サイドチャネル（時間・電力）への強さが要るとき。確かめていません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が素のオブジェクトでない、または決まった欄のほかを持つとき、INVALID_ARGUMENT で止めます。引数を読むこと自体が例外になるとき（getter や Proxy の罠）は INSPECTION_FAILED です。",
          "message が Uint8Array でないとき NOT_BYTES、16 MiB を超えるとき MESSAGE_TOO_LARGE で止めます。",
          "鍵が KeyObject でないとき（PEM・DER・JWK・bytes・CryptoKey・KeyObject を装ったもの・KeyObject の Proxy・欄が無い）、KEY_NOT_KEYOBJECT で止めます。",
          "KeyObject でも種類が違うとき（sign に公開鍵、verify に秘密鍵、共通鍵・EC・RSA・X25519・Ed448）、KEY_TYPE_UNSUPPORTED で止めます。",
          "key_id が Ref（印字できる ASCII の空白を除く 1〜200 文字、\"..\" を含まず、/ と \\ で始まらない）でないとき、INVALID_KEY_ID で止めます。",
          "signature が詰め物なしの正準の base64url で 64 bytes の文字列でないとき（詰め物・標準の base64・余りの bits が 0 でない・長さ違い）、INVALID_SIGNATURE で止めます。",
          "署名の 1 bit の反転、S + L の署名、ほかの message・ほかの鍵の署名は verified: false になります。",
          "引数の欄はそれぞれ一度だけ読み、getter が違う値を返しても、検めた値だけを使います。"
        ],
        "what_it_does_not_do": [
          "署名は「この秘密鍵を持つ誰かがこの bytes に署名した」ことだけを示します。承認・中身が正しいこと・受け入れてよいこと・署名者が誰かの証明ではありません。誰が署名してよいかの判断は、この部品の外です。",
          "この部品は秘密鍵を書き出さず（export を呼ばない）、保管せず、ログに出さず、結果に入れません。ただし、呼ぶ側が渡した KeyObject を消すことはしません（消すのは GC 任せ）。",
          "key_id は呼ぶ側の名札で、鍵と結びついているかを確かめません。どの鍵かを確かめるには public_key_spki_sha256 を使います。",
          "Ed25519 の計算そのものは Node.js の crypto（OpenSSL）に任せています。確かめたのは RFC 8032 の 7.1 の TEST 1〜3 と、実行時に作った鍵での Node の crypto との突き合わせまでです。",
          "verify の厳しさ（小さい位数の公開鍵・正準でない点など）は OpenSSL に従い、S + L の署名のほかの境目は確かめていません。B-02 との突き合わせもしていません。",
          "署名・指紋・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の署名ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/local-sign.js の sign({ message, private_key, key_id })・publicKeyFingerprint({ public_key })・verify({ message, public_key, signature }) を呼びます（どれも投げません）。sign は { ok: true, errors: [], algorithm: \"Ed25519\", key_id, signature, public_key_spki_sha256 } を返します（signature は 86 文字の base64url）。止めるときは { ok: false, errors: [{ code }] }。KeyObject は呼ぶ側が作って持ちます。副作用・状態・外の接続は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "B-13",
        "category": "B/暗号・信頼",
        "kind": "Adopted",
        "title": "重ね署名の確かめ（.amupkg）の受け入れ部品（Adopted）",
        "summary": "重ね署名の確かめ（.amupkg）の受け入れ部品（Adopted）は、.amupkg を Runtime に渡す前に、目録・自分用の署名・wi-t.com の重ね署名を、固定した起点（root-usb-1）の trust bundle の鍵と失効で確かめ、PASS か REFUSE を返す部品です。別の書き手が作った部品を、元の版（33f2b1c）の bytes を書き換えずに受け入れたもので、ERABAZU が足したのはこの説明書と工房のファズだけです。",
        "problem": "「確かめ済み」の裏で、どの鍵と失効を見たかは見えません。この部品は、重ね署名が無い・正しくないものを REFUSE にして通さず（fail-closed）、本物の鍵・取得先・起点を部品の中に固定して引数で差し替えさせず、trust の巻き戻りや同じ generation の別の bytes を断ります。結果には、見た trust と目録の digest を facts として添えます。",
        "use_when": [
          ".amupkg の bytes（Uint8Array）を Runtime に渡す前に、完全性・自分用の署名・重ね署名・trust・失効・結び付けを確かめ、verdict（PASS か REFUSE）・code・stage・facts を受け取りたいとき（verifyAmupkg。投げません）。",
          "PASS の後に Runtime で開いたとき、返った amupkg_digest が sha256: ＋ facts.manifest_digest と同じかを照らすための値を受け取りたいとき。この照らし合わせは使う側が行います。",
          "trust bundle を取りに行き、確かめに通ったものだけを store の port に保存したいとき（refreshTrust）。取れないときは、保存したものか、部品に焼き込んだ gen 10 で判定を続けます。",
          "trust の鮮度を区別したいとき。新しい受け入れは trust の next_update まで、受け入れ済みのものは next_update から 30 日まで動かし、過ぎれば COUNTERSIG_TRUST_STALE・COUNTERSIG_TRUST_EXPIRED で止めます。",
          "PASS したものだけを受け入れの記録に残し（recordAdmitted）、trust・受け入れた数・確かめの版を見たいとき（status）。",
          "どの元の版の bytes を受け入れたかを、元の commit（33f2b1c）と 36 本のファイルの sha256 で照らせる部品が要るとき。"
        ],
        "do_not_use_when": [
          "利用者が持ち込む .amupkg を、固定した見本（pinnedDigests）で通したいとき。固定した見本を守るのは、使う側のプログラムのコード署名で、部品はそれを確かめません。利用者が持ち込む .amupkg には使えません。",
          "本物の鍵で署名した重ね署名が PASS することを、工房で確かめた結果として扱いたいとき。工房はそれを確かめていません（工房の試験とファズは、使い捨ての起点と鍵だけを使います）。",
          "PASS を、承認・販売の許可・中身が正しいことの印として使いたいとき。PASS は「出所・完全性・確認の範囲を確かめた」ことだけです。",
          "ZIP のファイルの bytes をまるごと同じものに限りたいとき。PASS は目録と目録に載ったファイルと署名で決まるので、その用途では facts.archive_digest（ZIP 全体の sha256）を使います。",
          "利用者に見せる文（display の title_ja・detail_ja）を、そのまま本番の文として使いたいとき。display は仮の文です。",
          "元の版が直ったときに、自動で新しい版を使いたいとき。自動では追いません（auto_follow: false）。新しい版は写し直して検査し直します。trust の新しい generation（gen 11 以降）が出たときも検査し直します。",
          "Node.js v24.21.0 のほかの版で使うとき。元は Node.js 20 以上の ES module ですが、工房はほかの版を確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "確かめる順は、完全性 → 署名のファイルが目録に無いこと → 自分用の署名 → 固定した見本 → 重ね署名のファイル → 重ね署名の形 → trust（起点・鮮度）→ 重ね署名の鍵と署名 → 失効 → 結び付け、です。止めたときは、code と stage（integrity・self_signature・countersignature・trust・pinned）を返します。",
          "ZIP が上限（96 MiB・2000 件・1 件 64 MiB・合計 192 MiB・倍率 100）を超える・安全でない名前・読めないとき PKG_ARCHIVE_UNSAFE、載ってよいファイル・区分の決まりに合わないとき AMUPKG_SECTION_INVALID、目録の sha256・区分の digest が合わないとき AMUPKG_DIGEST_MISMATCH、目録の profile が扱えないとき AMUPKG_PROFILE_UNSUPPORTED、署名のファイルが目録に載っているとき MANIFEST_LISTS_SIGNATURE_FILE で止めます。",
          "自分用の署名が無いとき SELF_SIGNATURE_MISSING、目録に合わないとき SELF_SIGNATURE_INVALID、自分用の鍵が失効しているとき SELF_KEY_REVOKED で止めます。",
          "重ね署名のファイルが無い（COUNTERSIG_MISSING）・読めない（COUNTERSIG_UNREADABLE）・schema や purpose が違う・期限がある（期限は持たない決まり）・確認の範囲（scope_id・scope_version）が無い・重ね署名者が wi-t.com でない・この .amupkg の自分用の鍵と署名に結び付いていない（COUNTERSIG_SELF_SIGNER_MISMATCH）とき、それぞれのコードで止めます。",
          "重ね署名の鍵が trust bundle に無い（COUNTERSIGNER_KEY_UNKNOWN）・重ね署名専用でない・方式が違う・失効している、重ね署名の署名が合わない（COUNTERSIG_SIGNATURE_INVALID）、その重ね署名が失効している（COUNTERSIG_REVOKED）とき、REFUSE にします。",
          "trust bundle が確かめられない（取れない・保存も焼き込みも使えない）とき COUNTERSIG_TRUST_UNAVAILABLE で止めます。trust の取得は 1 ファイル 256 KiB・10 秒までで、redirect を追いません。trust の巻き戻りと、同じ generation の別の bytes は断ります。",
          "正しい .amupkg を 1 回ごとに 1 つの壊し方（目録の対象のファイルの書き換え・消し・足し、重ね署名の差し替え、別の鍵での署名の作り直し、ランダムな bytes の書き換えなど）で変えて答えと突き合わせる 1,000,000 回のファズを工房の検査で走らせ、不一致と fail-open が 0 でした（Node.js v24.21.0）。"
        ],
        "what_it_does_not_do": [
          "PASS は「出所・完全性・確認の範囲を確かめた」ことだけで、承認・販売の許可・中身の正しさではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "部品が確かめるのは、目録と、目録に載ったファイルと、署名だけです。ZIP の中のファイルの外にある bytes（末尾など）は確かめの対象外で、Runtime も使いません。ファイルの bytes をまるごと同じにしたいときは facts.archive_digest（末尾に足すと変わる）を使います。",
          "固定した見本（pinnedDigests）を守るのは使う側のプログラムのコード署名で、部品はそれを確かめません。利用者が持ち込む .amupkg には使えません。",
          "本物の trust（gen 10）には重ね署名の鍵 wi-t-countersigner-ed25519.v1 が載っていて、その鍵で署名されていない重ね署名は COUNTERSIG_SIGNATURE_INVALID になります。部品に焼き込んだ trust も gen 10 です。ただし、本物の鍵で署名した重ね署名が PASS することは、工房では確かめていません。工房の試験とファズは本物の秘密鍵を使わず、使い捨ての起点と鍵だけを使います。",
          "これは ERABAZU が作った確かめの実装ではありません。別の書き手が作った部品を、bytes を書き換えずに受け入れたものです（36 本の sha256 と元の出所の記録で照らします）。中身・確かめの順番・止めるコード・API は元の設計のままです。受け入れたファイルには LICENSE・LICENSES/CC0-1.0.md・NOTICE もあり、NOTICE は元の書き手が足したものです。",
          "Runtime の実行ファイルとの通し（flow）は、工房の検査では動かしていません（実行ファイルが無い）。元のリポジトリの CI（run 37588955237）で PASS しています。元の試験は 54 件（pass 53・skip 1）です。",
          "store の port に trust と受け入れの記録を書き、fetch の port で trust の 4 つのファイルを読みます（LOCAL_REVERSIBLE）。部品のメモリーには状態を持たず、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/index.mjs（ES module）の createCountersignatureVerifier({ store, fetch?, now?, log?, pinnedDigests? }) で instance を作り、await instance.verifyAmupkg(bytes, { admit? }) を呼びます。store（read・write）は必須の port、fetch（null なら取りに行かない）・now・log は省けます。返すのは { verdict, code, stage, facts, display } です。factory の引数の形が違うときと、bytes が Uint8Array でないときだけ TypeError を投げます。依存は Node.js の組み込み（node:crypto・WebCrypto）だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": [
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/bytes.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/canonical-json.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/purposes.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/roots.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-index.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/verify-core.mjs",
              "license": "MPL-2.0"
            }
          ]
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-01",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "オブジェクト保管 — オブジェクト保管継手（Tsugite）",
        "summary": "オブジェクト保管継手（Tsugite）は、ファイルや blob の保存（put）と読み出し（get）を、外部の object backend へ中継する継手（つぎて）です。put は検めた要求だけを送り、backend が返す受領（ObjectReceipt）を契約どおりに照合します。get は返ってきた中身（bytes）の digest を自分で計算し、期待値と一致して受領も照合できたときだけ中身を渡します。一つでも合わなければ受理しません（fail-closed）。",
        "problem": "「保存した」で終わらせてしまうこと、つまり backend の受領や申告をそのまま信じて、化けた中身や偽った保存物を正しいものとして扱ってしまうことを防ぎます。読み出した中身は backend の「一致した」という申告だけでは検証済みにせず、自分で digest を計算して突き合わせます。",
        "use_when": [
          "ファイルや blob を外部の object backend に保存し、読み出したいとき。",
          "読み出した中身を、backend の申告ではなく自分で計算した digest で確かめてから使いたいとき。",
          "受け付ける content-type と大きさの上限を決め、中身の種類を偽ったもの（magic bytes と合わないもの）を通したくないとき。",
          "同じ中身を二重に保存したくないとき、また一度書いたら上書きしない（WORM）対象を守りたいとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
          "部分書込や偽の受領が出たら、人が確かめるまで保存を止めたいとき。"
        ],
        "do_not_use_when": [
          "application/pdf・image/png・image/jpeg・text/plain・application/json 以外の content-type を、中身の種類まで確かめて扱いたいとき。magic bytes で確かめられるのはこの 5 種だけで、確かめられない型は通しません。",
          "保持期間・TTL（C-08）やアーカイブ（C-09）のための削除・ライフサイクルの操作が要るとき。この継手には含まれていません（後続）。",
          "put の成功を「中身が正しいと確かめた」と扱いたいとき。put の成功は中身の検証済みではありません（content_verified: false）。",
          "保存の成功を、承認や決裁として扱いたいとき。どの成功も承認ではありません。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの object store につなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、side effect の条件に反するものは送りません。",
          "pin した backend の版を検めます。pin していない backend の版（backend_profile_revision）への要求は送りません。",
          "中身の種類と大きさを検めます。magic bytes と合わない content-type（種類の偽り）、上限を超える大きさは送りません。",
          "二重保存と上書きを検めます。同じ object_ref・同じ digest は二重に保存せず（ALREADY_STORED）、WORM の対象への上書きは送りません。",
          "get で読み出した中身の digest を自分で計算し、期待値と合わなければ中身を渡しません（GET_REJECTED）。読み出し時の改ざんを拒否します。",
          "受領（ObjectReceipt）を照合します。request_digest を偽った受領、部分書込（MISMATCH）があれば受理せず、以後は停止します（HALTED）。",
          "backend の障害（500 の応答、書いた後の失敗）は UNKNOWN とし、保存済みとして扱いません。再送は照会（lookup）で解きます。",
          "秘密の渡し方を検めます。SecretRef は secret-ref/v2 の場所だけを受け付け、SecretRef の隣に置いた生の秘密や、HUMAN_CREDENTIAL の種類は拒否します。"
        ],
        "what_it_does_not_do": [
          "化けていないかを決めるのは核（integrity-facts）の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "put の成功は中身の検証済みではなく（content_verified: false）、どの成功も承認ではありません（approval: false）。",
          "実 backend を含みません。どの object store につなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
          "保持期間・TTL（C-08）やアーカイブ（C-09）の削除・ライフサイクルの拡張は含みません。",
          "magic bytes で中身の種類を確かめられるのは、application/pdf・image/png・image/jpeg・text/plain・application/json だけです。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "object store の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/object-store/v1 でつなぎます。createObjectStoreClient({ cfg, port }) の put(ObjectPutRequest, bytes)・get(ObjectGetRequest) は、STORED・GET_VERIFIED・GET_REJECTED・REFUSED・UNKNOWN・HALTED などを返します（どれにも approval: false）。利用側が用意するもの: object backend への port（put・get、任意で lookup）。副作用は WORM が EXTERNAL_IRREVERSIBLE、OVERWRITE が EXTERNAL_REVERSIBLE、get は NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-02",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "保管完全性検証 — 保管完全性継手（Tsugite）",
        "summary": "保管完全性継手（Tsugite）は、保管先から読み戻した物が化けていないかを、書いたときの記録（recorded）と読み戻したときの観測（observed）の突き合わせで確かめる継手（つぎて）です。ハッシュ・大きさ・ハッシュアルゴリズムのどれか一つでも合わなければ、信頼しません（fail-closed）。ハッシュの計算と保管先への読み書きは、呼び出し側が行います。",
        "problem": "保管した物が、気づかないうちに化けてしまうこと（沈黙のビット腐敗）や、改ざん・すり替えを見逃して、そのまま使ってしまうことを防ぎます。大きさが一致しただけでは内容の一致とはみなさず、ハッシュの一致を別に求めます。",
        "use_when": [
          "保管先から読み戻した物が、書いたときと同じかを確かめたいとき（checksum や ETag で化けていないか）。",
          "ネットワークの中継（proxy）を置かずに、呼び出し側のライブラリとして完全性を確かめたいとき。",
          "採択したハッシュアルゴリズムだけを受け付け、それ以外のアルゴリズムの結果を信頼したくないとき。",
          "ハッシュと大きさを、自分の側ですでに計算・取得していて、その突き合わせだけを任せたいとき。"
        ],
        "do_not_use_when": [
          "ハッシュの計算や、保管先への書き込み・読み戻しまでこの継手にさせたいとき。これらは含まれておらず、呼び出し側が行って結果を渡します。",
          "記録（recorded）を残していない物を確かめたいとき。書いたときの hash・size・algo が無ければ、突き合わせられず、信頼しません。",
          "特定の保管 API の ETag の形式に、今すぐ合わせて使いたいとき。実際の保管 API と ETag の形式は、配備するときに config で決めます。",
          "SHA-256 以外のハッシュで確かめたいとき。確かめた範囲は SHA-256 の内容ハッシュです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "ハッシュを突き合わせます。記録と観測のハッシュが合わなければ（ビット腐敗・改ざん）、信頼しません（ok: false）。",
          "大きさを突き合わせます。記録と観測の大きさが合わなければ、信頼しません。",
          "大きさが同じでも、内容のハッシュが違えば信頼しません（大きさだけの一致では通しません）。",
          "ハッシュアルゴリズムを検めます。記録と観測でアルゴリズムが違うもの、採択していないアルゴリズムは信頼しません。",
          "欠落を検めます。ハッシュや大きさが欠けていれば、信頼しません。"
        ],
        "what_it_does_not_do": [
          "ハッシュを計算しません。保管先への書き込みも読み戻しもしません。呼び出し側が行い、その結果（recorded と observed）を渡します。",
          "backend につなぐ port を持ちません。この継手は、渡された値を突き合わせる純粋な関数だけです。",
          "require_size を false にすると、大きさの一致を求めなくなります（既定は true）。",
          "実際の保管 API と ETag の形式は決めていません。配備するときに config で決めます。",
          "完全性の事実を核としてまとめ、署名の対象や来歴に継ぐのは核（integrity-facts・provenance-facts）の側です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "保管 API の使い方の雛形ではありません。"
        ],
        "integration": "入口は src/guards.js の verifyIntegrity(cfg, recorded, observed)（純粋な関数）です。recorded に書いたときに記録した { hash, size, algo }、observed に読み戻したときに観測した { hash, size, algo } を渡すと、一致すれば { ok: true, errors: [] }、合わなければ { ok: false, errors: [...] } を返します。cfg は allowed_algos（採択したアルゴリズム）と require_size（既定 true）。port は無く、ハッシュの計算と保管先への読み書きは利用側が用意します。実行環境は Node.js（依存パッケージなし）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-03",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "トランザクション書込 — トランザクション書込継手（Tsugite）",
        "summary": "トランザクション書込継手（Tsugite）は、pin した backend・許可した論理の target・操作ごとの本体の digest を検めてから、全部か無しか（ALL_OR_NOTHING）の transaction を DB へ送り、DB が返す受領（TransactionalWriteReceipt）を照合する継手（つぎて）です。部分コミットは異常として停止し、ABORTED は外への効果が無いと照合できたときだけ受け取ります（fail-closed）。",
        "problem": "DB に「保存されたはず」と思い込んでしまうこと、つまり半分だけ書けた transaction を成功として扱ってしまうことを防ぎます。COMMITTED はすべての操作が入ったと照合できたとき、ABORTED は何も入っていないと照合できたときだけ受け取ります。",
        "use_when": [
          "複数の書き込みを、一貫性を保ったまま DB に書きたいとき（すべて入るか、何も入らないか）。",
          "DB が返す受領をそのまま信じず、要求（request_id・request_digest）と target の一致を照合してから、書けたと扱いたいとき。",
          "書き込み先を、pin した backend と許可した論理の target に限りたいとき。ファイルシステムの路を target にさせたくないとき。",
          "同じ要求を二度コミットしたくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
          "部分コミットや偽の受領が出たら、人が確かめるまで書き込みを止めたいとき。"
        ],
        "do_not_use_when": [
          "同じ契約 storage-persistence/v1 に属する C-05〜C-09 の機能が要るとき。この継手には含めていません。",
          "書き込みの成功を、承認や決裁として扱いたいとき。書けたことは承認ではありません。",
          "すぐに使える DB がほしいとき。実 DB は含まず、どの backend につなぐかはまだ決まっていません。",
          "ファイルシステムの路（先頭の / や \\、..）に書きたいとき。target にしません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "送り先を検めます。pin していない backend、許可していない target、ファイルシステムの路の target への要求は送りません。",
          "操作と本体を検めます。本体の改ざん（digest の不一致）、本体の過不足、同じ操作の重複、操作の数や本体の大きさが上限を超えるものは送りません。",
          "部分コミットを受理しません。COMMITTED は全件、ABORTED は 0 件で外への効果が無いことを求め、外れれば受理せず、以後は停止します（HALTED）。",
          "side effect を検めます。受領の side effect は要求の宣言から減らすことだけを認め、増やした受領は受理しません。COMMITTED は意図した side effect と一致する必要があります。",
          "受領（TransactionalWriteReceipt）を照合します。request_digest を偽った受領、別の target の受領は受理せず、以後は停止します（HALTED）。",
          "DB の障害は UNKNOWN とし、書けたとして扱いません。同じ request_id は二度コミットせず、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "一貫性を決めるのは核（reconcile-facts・policy-facts）の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "書けたことは承認ではありません（approval: false）。",
          "実 DB を含みません。どの backend につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "同じ契約 storage-persistence/v1 の C-05〜C-09 は含みません。優先順位の案では、継手を作らない（decision-only）か後回し（DEFER）です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "DB クライアントの雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createTxClient({ cfg, port }) の write(TransactionalWriteRequest, payloads)（payloads は { op_ref: bytes }）は、COMMITTED・ABORTED・REFUSED・UNKNOWN・HALTED などを返します（どれにも approval: false）。利用側が用意するもの: DB への port（commit と、任意で lookup）。副作用は要求で宣言する EXTERNAL_REVERSIBLE か EXTERNAL_IRREVERSIBLE（ABORTED は NONE か LOCAL_REVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-04",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "追記専用台帳 — 監査台帳継手（Tsugite）",
        "summary": "監査台帳継手（Tsugite）は、改変不能なイベント履歴を追記専用（append-only）の backend に積むときに、pin した台帳・通番（sequence）・二重追記の有無を検めてから送り、返る受領（AuditAppendReceipt）を照合できたときだけ台帳の先頭（head）を進める継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "外部の追記専用の保管先にイベントを積み重ねるとき、手元の台帳の先頭と保管先の実際がずれること（通番の巻き戻し・飛び、同じイベントの二重の追記、障害で結果が分からない書き込み）を防ぎます。受領を照合できない追記では台帳の先頭を進めないので、台帳の続き方を信頼できる状態に保てます。",
        "use_when": [
          "改変不能なイベント履歴を、外部の追記専用 backend に 1 件ずつ積み重ねたいとき。",
          "決まった 1 つの台帳（ledger_id・ledger_profile_revision・contract_revision で pin したもの）にだけ追記したいとき。",
          "通番が巻き戻ったり飛んだりしない、一本の続き方を保ちたいとき。",
          "同じイベントを二重に追記したくないとき。二重かどうかは event_id と封筒の digest で見分け、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
          "台帳の先頭・未解決の送信・停止の有無を、手元で確かめながら追記を進めたいとき（state() が { head, pending, halted } を返します）。"
        ],
        "do_not_use_when": [
          "書き換えや削除ができる保管先として使いたいとき。この継手は追記だけを扱います。",
          "イベントの本文そのものを台帳に積みたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
          "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
          "追記の成功を、承認や決裁として扱いたいとき。追記の成功は承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin した台帳を検めます。pin していない台帳への追記は送りません。",
          "通番（sequence）を検めます。台帳の先頭から見て通番が巻き戻っている・飛んでいるときは、送らず、受理しません。",
          "二重の追記を止めます。同じ event_id・同じ内容がもう追記済みなら backend に送らず ALREADY_APPENDED を返し、同じ event_id で内容が違うときは REFUSED を返します。",
          "未解決の送信があるあいだは、次の封筒を送らず REFUSED を返します。結果が分からなかった送信は照会（lookup）で解きます。",
          "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
          "受領（AuditAppendReceipt）の台帳・通番・event_id・対象が、送った封筒と食い違うときは受理せず、台帳の先頭を進めません。",
          "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
          "本文が混じった封筒、封筒の大きさの上限（max_envelope_bytes）を超えるイベント、種類を偽った封筒（content-type の偽り）を拒否します。"
        ],
        "what_it_does_not_do": [
          "台帳が改変できないかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
          "追記の成功は承認ではありません（approval: false）。",
          "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
          "透明性ログ（Merkle の包含証明）は扱いません。",
          "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
          "C-04 専用の部品ではありません。H-01（改変不能監査ログ）・H-08（改ざん検知チェーン）・H-02（アクセスログ）と同じ 1 つの部品が担い、試験と検査も共通です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する台帳と封筒の上限。state() は { head, pending, halted }。利用側は追記専用 backend への port（append、任意で lookup）を用意します。副作用は EXTERNAL_IRREVERSIBLE（取り消せない書き込み）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-05",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "冪等書込（WAL） — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend への冪等書込（IdempotentWrite）の要求を、pin した backend・許可した論理の target・書込の中身の上限で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。外への効果は高々 1 回で、一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "保管先への書き込みを再送したときに同じ内容を二重に書いてしまうこと、同じ冪等キーで別の中身を書いてしまうこと、そして backend の receipt を確かめないまま「書けた」と扱ってしまうことを防ぎます。",
        "use_when": [
          "再送があっても、保管 backend への書き込みの外への効果を高々 1 回にしたいとき。",
          "重複で効果なし（DEDUP_HIT）と返ったときも、保管されている中身が送った中身と同じかを確かめてから受け取りたいとき。",
          "書込の中身の digest を自分で確かめ、大きさの上限を超える中身を送りたくないとき。",
          "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
        ],
        "do_not_use_when": [
          "トランザクション書込が要るとき。C-03 のトランザクション書込は別の部品（transactional-write-tsugite）です。",
          "重複かどうかの決定をこの継手に任せたいとき。重複の検出は核（reconcile-facts）の役目です。",
          "ファイルシステム上の場所を書き先にしたいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "書けたことを、承認や決裁として扱いたいとき。書けたことは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "同じ冪等キーで別の中身を書く要求は送りません（REFUSED）。",
          "書込の中身を検めます。大きさの上限を超える中身、digest と合わない（改ざんされた）中身は送りません。",
          "重複（DEDUP_HIT）の receipt が新しい効果を伴っていれば受理しません。DEDUP_HIT は効果なし（NONE）でなければなりません。",
          "保管された中身（stored_digest）が送った中身と違えば、DEDUP_HIT でも受理しません。同じキーで別の中身は異常として扱います。",
          "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
          "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。"
        ],
        "what_it_does_not_do": [
          "重複かどうかを決めるのは核（reconcile-facts）の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "書けたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその冪等書込の面で、F-04（冪等キー処理）も同じ面で担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "保管サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request, bytes) に IdempotentWrite の要求と書込の中身 bytes（必須）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に dedup_result を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、backend・target・上限の設定。副作用は EXTERNAL_*（DEDUP_HIT は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-06",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "バックアップ・スナップショット — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend へのスナップショット（Snapshot）の要求を、pin した backend と許可した論理の target で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "スナップショットの要求を送るとき、許可していない対象を送ってしまうこと、そして backend の receipt が本当にその対象のものかを確かめないまま「取れた」と扱ってしまうことを防ぎます。この継手が照合できたスナップショットだけが、模擬の復元（C-07）の元になります。",
        "use_when": [
          "保管の backend に論理の target のスナップショットの要求を送り、receipt を照合できてから、取れたことを事実として受け取りたいとき。",
          "スナップショットの receipt を、その snapshot_ref の中身の digest（content_digest）と対にして照合したいとき。",
          "照合できたスナップショットだけを、模擬（SYNTHETIC）・dry-run の復元の元として扱いたいとき。",
          "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
        ],
        "do_not_use_when": [
          "スナップショットから実際に戻すこと（本番の PITR）まで求めるとき。実際に壊す復元は範囲外で、復元は模擬・dry-run だけです。",
          "ファイルシステム上の場所の複製を取りたいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "取れたことを、承認や決裁として扱いたいとき。取れたことは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
          "スナップショットの receipt の効果は、取り消せる外部への効果（EXTERNAL_REVERSIBLE）までです。それより効果を増やす receipt は受理しません。",
          "照合できたスナップショットを state() の snapshots に残し、照合していないスナップショットからの復元の要求は送りません。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
          "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
        ],
        "what_it_does_not_do": [
          "スナップショットが照合できたことは、そこから実際に戻せることを確かめたものではありません。実際に壊す復元（本番の PITR）は範囲外です。",
          "何をいつ取るかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "取れたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はそのスナップショットの面です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "保管サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Snapshot の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に snapshot_ref・snapshot_state を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する backend・許可する target の設定。副作用は取り消せる外部への効果（EXTERNAL_REVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-07",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "復旧・PITR（模擬・dry-run だけ） — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、復旧（Restore）の要求を模擬（SYNTHETIC）と dry-run に限って検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。実際に壊す復元（本番の PITR）はせず、その要求は送りません。元にできるのは、この継手が照合したスナップショットだけです（fail-closed）。",
        "problem": "復元の要求を送るとき、照合していないスナップショットから戻そうとすること、dry-run なのに「適用した」とする結果を信じてしまうこと、実際に壊す復元の要求が送られてしまうことを防ぎます。この継手が扱うのは模擬・dry-run の復元だけで、実際にある時点へ戻すことはしません。",
        "use_when": [
          "照合済みのスナップショットを元に、模擬（SYNTHETIC）または dry-run の復元の要求を送り、その receipt を照合したいとき。",
          "dry-run の復元の結果として、効果なし（NONE）の receipt だけを受け取りたいとき。",
          "復元の元を、この継手が CREATED と照合したスナップショットに限りたいとき。"
        ],
        "do_not_use_when": [
          "実際にある時点へ戻したいとき（本番の PITR）。実際に壊す復元は範囲外で、契約も模擬・dry-run だけを定めます。この継手はその要求を送りません。",
          "事故のときに実際に復旧する手段として使いたいとき。この継手は実際の復旧をしません。",
          "この継手が照合していないスナップショットから戻したいとき。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "実際に壊す復元のモードの要求は送りません。",
          "この継手が CREATED と照合していないスナップショットからの復元の要求は送りません。",
          "dry-run の要求に「適用した」とする receipt は受理しません。APPLIED_SYNTHETIC は模擬（SYNTHETIC）の要求のときだけ受け取ります。",
          "復元の receipt の効果は、効果なし（NONE）か手元で取り消せる効果（LOCAL_REVERSIBLE）までで、dry-run は NONE だけです。効果を増やす receipt は受理しません。",
          "request_digest の偽り、別の target の receipt は受理せず、以後停止します（HALTED）。",
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
          "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
        ],
        "what_it_does_not_do": [
          "実際の復旧はしません。実際に壊す復元（本番の PITR）は範囲外で、契約も模擬・dry-run だけを定めます。DONE に添える restore_state は、模擬・dry-run の復元の結果です。",
          "ファズは backend を叩かず、試験は保管 backend の代役を使っています。実際の backend である時点へ戻せることは確かめていません。",
          "何をいつ戻すかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "模擬の復元が照合できたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその模擬の復元の面です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Restore の要求（模擬か dry-run）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に restore_state を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）。副作用は NONE か LOCAL_REVERSIBLE（dry-run は NONE）で、実際の復旧は含みません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-08",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "保持・TTL — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、保持（Retention）の要求（EVALUATE・RETAIN・EXPIRE）を、pin した保持方針と論理の target で検めてから送り、receipt を契約どおりに照合する継手（つぎて）です。取り消せない削除（EXPIRE）は、設定 allow_expire（既定 false）で許し、リーガルホールドの無い対象にだけ送ります（fail-closed）。",
        "problem": "保持期限による削除を backend へ送るとき、許していない削除やリーガルホールド中の対象の削除を送ってしまうこと、期限切れの判定だけのはずが削除されたと扱うこと、backend の receipt を確かめないまま「消せた」と扱うことを防ぎます。",
        "use_when": [
          "保持方針に沿った削除（EXPIRE）の要求を、設定で許したときだけ backend へ送りたいとき。",
          "リーガルホールドの事実（核・人の決定）を受け取り、以後その対象の EXPIRE を送らないようにしたいとき。",
          "削除せずに、期限切れかどうかの判定（EVALUATE）だけを backend に求めたいとき。",
          "消せたこと（deleted: true）を、EXPIRE の receipt が EXPIRED と照合できたときだけ事実として扱いたいとき。"
        ],
        "do_not_use_when": [
          "保持期限やリーガルホールドをこの継手に決めさせたいとき。その決定は核と人の役目で、この継手は事実を受け取って守るだけです。",
          "期限が来たら自動で消す仕組みそのものがほしいとき。この継手は渡された保持の要求を検めて送るだけで、EXPIRE は設定 allow_expire が true でなければ送りません。",
          "期限で別の階層へ移したいとき。階層の移動は Archive の要求で、C-09 アーカイブ・階層化の面です。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定 allow_expire が true でないとき、EXPIRE を送りません。",
          "リーガルホールド中の対象には EXPIRE を送りません。",
          "EVALUATE の receipt が削除を伴っていれば受理しません。EVALUATE の EXPIRED は期限切れの判定だけで、削除ではありません。",
          "RETAIN に EXPIRED を返す receipt は、異常として受理しません。",
          "pin した一覧にない保持方針の要求は送りません。",
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。backend の障害は UNKNOWN とし、同じ request_id を二度送りません。"
        ],
        "what_it_does_not_do": [
          "保持期限の決定・リーガルホールドの決定はしません。核（redact-facts・policy-facts）と人の役目で、この継手はその事実を受け取って守るだけです。",
          "消せたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその保持の面で、H-06（保持強制）も同じ面で担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "保管サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Retention の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に decision・deleted・effect を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する保持方針・allow_expire の設定。副作用は EXPIRE が EXTERNAL_IRREVERSIBLE、ほかは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-09",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "アーカイブ・階層化 — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、保管の backend への階層の移動（Archive）の要求を、pin した階層と許可した論理の target で検めてから送り、backend が返す receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "データを別の階層へ移す要求を送るとき、pin していない階層や許可していない対象へ送ってしまうこと、そして backend の receipt を確かめないまま「移せた」と扱ってしまうことを防ぎます。",
        "use_when": [
          "古いデータを、pin した階層へ移す要求を backend へ送りたいとき。",
          "移した結果（transition_state・archive_ref）を、receipt を照合できてから事実として受け取りたいとき。",
          "backend の障害で結果が分からないとき、同じ request_id を二度送らず、照会（lookup）で解きたいとき。"
        ],
        "do_not_use_when": [
          "どのデータをいつ移すかをこの継手に決めさせたいとき。決定は核の役目で、この継手は送る前と受け取った後の検めだけです。",
          "取り消せない削除を伴わせたいとき。アーカイブの効果は取り消せる外部への効果（EXTERNAL_REVERSIBLE）までで、取り消せない削除は保持（C-08）の EXPIRE の扱いです。",
          "ファイルシステム上の場所へ移したいとき。target は論理の target だけで、ファイルシステムの target は拒否します。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していない階層への移動の要求は送りません。",
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "アーカイブの receipt の効果は、取り消せる外部への効果（EXTERNAL_REVERSIBLE）までです。それより効果を増やす receipt は受理しません。",
          "request_digest の偽り、別の target の receipt は受理せず、以後停止します（HALTED）。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。",
          "backend の障害は UNKNOWN とし、同じ request_id を二度送りません。未解決の送信があるときは REFUSED を返し、backend に送りません。"
        ],
        "what_it_does_not_do": [
          "何をいつ移すかの決定は核の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "移せたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "この項目だけの別の部品ではありません。C-05〜C-09 と H-06・F-04 は契約上 1 つの部品（tsugite/storage-persistence/lifecycle）で、この項目はその階層の移動の面です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "保管サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に Archive の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に transition_state・archive_ref を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、pin する backend・階層（tiers）・許可する target の設定。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-10",
        "category": "C/保管・永続化",
        "kind": "Component",
        "title": "生データの保管（追記だけ） — raw の保管（追記だけ・内容アドレス・区画分け）の部品（Component）",
        "summary": "raw の保管の部品（Component）は、取得した raw を型にはめずに、retention_class（pii・business・evidence）ごとに別の保管先へ追記だけで置く部品です。取得の記録の部品(Primitive)（N-09）で fetch.json を作り、渡した bytes が記録の name・byte_size・sha256 と合うことを確かめ、payload を記録の順に、最後に fetch.json を冪等書込の継手（C-05）で書きます。冪等キーはパスです。read は C-01 の継手で期待する sha256 を添えて読み、digest が合ったときだけ bytes を返します。削除・上書きの操作はありません。",
        "problem": "「保存してある」の裏で、元のまま残っているかは誰も確かめません。この部品は、記録と合わない bytes を置くこと、一度置いたキーを別の中身で上書きすること、PII をほかの区画と混ぜること、読んだ bytes が置いたものと違うのに使ってしまうことを防ぎます。",
        "use_when": [
          "取得したファイル（1〜10000 個）と fetch.json を、raw/<source>/<folder_stamp>/payload/<name> と raw/<source>/<folder_stamp>/fetch.json のキーで、追記だけで 1 回だけ置きたいとき（store）。",
          "保持区分ごとに保管先を分け、PII の区画には、ほかの区画と別の backend・別のクライアントを当てたいとき。どの書込も、その取得の retention_class の区画の target にだけ送ります。",
          "PARTIAL の取得も、取れた分をほかと同じに残したいとき。続きは次の取得で、別の fetched_at の別のフォルダーになります。",
          "置いた raw を、期待する sha256 を添えて読み、継手の検めの後に自分でも sha256 を計算し直して、合ったときだけ bytes の写しを受け取りたいとき（read）。",
          "結果の分からなかった書込を、同じ要求で送り直して残りの書込を続けたいとき（resolvePending）。同じ取得をもう一度 store すれば、書けた分は DEDUP_HIT で続きから書けます。"
        ],
        "do_not_use_when": [
          "保持の期間を数える・期限で消す・PII の期間の決まりを守らせることまで任せたいとき。この部品は区画を分けて置くだけです。保持と削除は backend の方針（C-08 など）の側です。",
          "取得した中身がプラットフォームの中身と同じかを確かめたいとき。確かめるのは、渡した bytes が N-09 の記録の name・byte_size・sha256 と合うことだけです。",
          "PII の区画が本当に別のストレージであることを確かめたいとき。確かめるのは backend_profile_ref の文字列とクライアントのオブジェクトが違うことまでで、2 つの名前が同じ物を指していても見分けられません。",
          "ファイルの大きさの上限をこの部品に任せたいとき。上限は C-05 の継手の max_payload_bytes で決めます。大きなファイルは送るたびに写しを作ります。",
          "ほかのプロセスや再起動の前に書いたキーまで、この部品に RAW_CONFLICT で見分けさせたいとき。控えはプロセスの中だけで、そのキーは継手と backend が断ります（どちらでも上書きはしません）。",
          "同じ request_prefix の 2 つのインスタンスで、同じクライアントを使いたいとき。継手が同じ request_id を ALREADY_DONE で答え、別の要求が前の答えで返ります。request_prefix はクライアントごとに一意にします。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・区画が 3 つちょうどでない・2 つの区画が同じ保管先・pii がほかの区画と同じ backend かクライアント・port の関数が無いとき、INVALID_OPTIONS で止めます。",
          "操作の入力の形が違うとき（欄の過不足、files が 1〜10000 個の素の配列でない、name の重なり、bytes が Uint8Array でない、read の key が raw/ で始まらない など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "N-09 が記録を作らないとき FETCH_RECORD_REFUSED（N-09 の errors を添える）、N-09 が例外を投げたとき FETCH_RECORD_PORT_FAILED、答えが食い違うとき（canonical の sha256・正準形・schema・入力の欄・folder_stamp・files の形）FETCH_RECORD_INVALID で、何も送りません。",
          "渡した files が記録の files と合わないとき、FILE_MISMATCH で何も送りません。errors[0] に name と reason（MISSING・SIZE・DIGEST・EXTRA）を添えます。",
          "書込の計画のキーに、この部品が前に別の sha256 を送っているとき、RAW_CONFLICT で何も送りません（追記だけ・上書きしない）。",
          "書込の結果が分からないとき STORE_UNKNOWN で同じ要求を保ち、保留の間の store を STORE_PENDING で止めます。継手が送らなかったとき WRITE_REFUSED、停止・receipt の照合の失敗・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED で、次へ進みません。",
          "read で、保管物が無いとき OBJECT_NOT_FOUND、digest が合わないとき（継手の GET_REJECTED か、自分で計算し直した sha256 の違い）DIGEST_MISMATCH、結果が分からないとき READ_UNKNOWN、送らなかったとき READ_REFUSED、停止・形の違いのとき READ_FAILED で、bytes を返しません。",
          "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さない・投げたとき CLOCK_FAILED で何も送らず、前の操作が終わっていないとき BUSY で止めます。"
        ],
        "what_it_does_not_do": [
          "raw の保管の部品は継手ではありません。自分の Request/Receipt は無く、書込と読出しという外への効果は、束ねた C-05・C-01 の継手がそれぞれの契約で起こします。この部品自身は外へ触れません。",
          "束ねた部品の意味（記録できた ≠ 取得の正しさ・書けた ≠ 受け入れ・読めた ≠ 中身の正しさ）は、それぞれの部品の側に残ります。",
          "削除・上書きの操作はありません。送るのは IdempotentWriteRequest と ObjectGetRequest だけです。保持の期間は数えず、消しません。",
          "書けたこと・読めたことは、中身の正しさでも受け入れでも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "C-05 のキーと C-01 の object_ref が同じ保管物を指すか（書いた raw を read で読めるか）は backend profile の役目です。試験では同じ保管物を指す代役で確かめました。",
          "本物の N-09・C-05・C-01 には、この部品の試験では接続していません。単体試験とファズは代役で、本物との結線は別の結線の検めで確かめます。状態（送ったキーの控え・保留）はプロセスの中だけで、再起動で消えます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のデータレイク・オブジェクトストレージの雛形ではありません。"
        ],
        "integration": "src/raw-store.js の createRawStore(options) で作ります。受け取るのは partitions { pii, business, evidence }・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。操作は store・read・resolvePending・status で、どの結果にも approval: false が付きます。利用側が用意する port は fetchRecord（N-09）、区画ごとの write.<class>.submit（C-05 の継手のクライアント）と read.<class>.get（C-01 の継手のクライアント）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "C-11",
        "category": "C/保管・永続化",
        "kind": "Component",
        "title": "区分ごとの世代バックアップ — 区画ごとの世代バックアップと復元の検めの部品（Component）",
        "summary": "区画ごとの世代バックアップと復元の検めの部品（Component）は、保持区分（pii・business・evidence）ごとに、別の保管先・別のクライアント・別の世代の数え方でバックアップを取る部品です。backup はスナップショットの継手（C-06）で作らせ、照合できたものだけを世代の記録として冪等書込の継手（C-05）で残します。verifyRestore は模擬の復元（C-07、DRY_RUN か SYNTHETIC）だけを送り、復元した中身の digest が世代の content_digest と C-02 の継手で一致したときだけ「復元できると確かめた」にします。",
        "problem": "バックアップは戻すときまで試されません。この部品は、照合できないスナップショットを世代にすること、PII をほかの区画と混ぜること、世代を上書きすること、確かめていないバックアップを「復元できる」と扱うことを防ぎます。",
        "use_when": [
          "区画ごとにスナップショットを作らせ、照合できたものを次の世代として backup/<class>/<seq>.json に 1 回だけ記録したいとき（backup）。世代の番号は last_seq + 1 から、書けた世代だけで数えます。",
          "記録した世代から SYNTHETIC の模擬の復元を送り、readRestored が返す復元した中身の digest を、世代の content_digest と照合したいとき（verifyRestore）。",
          "世代が keep_generations を超えたことを、over_limit の事実として受け取りたいとき。古い世代は消しません。",
          "区画ごとの世代と確かめの結果（restore_verified）・保留を一覧したいとき（status）。",
          "結果の分からなかった送信を、その区画だけ保留にし、同じ要求で送り直したいとき（resolvePending）。保留の間も、ほかの区画は止まりません。"
        ],
        "do_not_use_when": [
          "本番への復元（実際に壊す復元）をしたいとき。送るのは DRY_RUN か SYNTHETIC だけで、本番で復元できるかは確かめていません。",
          "古い世代を期限で消したいとき。keep_generations は over_limit の事実を返すだけで、消すのは backend の保持（C-08 など）の役目です。",
          "取ったばかりのバックアップを「復元できる」と扱いたいとき。verifyRestore の digest の照合（SYNTHETIC・readRestored あり）が済むまで restore_verified: false です。readRestored が無い・DRY_RUN のときは digest_verified: null です。",
          "スナップショットの content_digest が中身を正しく表しているか、readRestored が本当に復元した中身から digest を計算したかまで確かめたいとき。この部品は返った値どうしを照合するだけです。",
          "再起動の前に取った世代を、そのまま verifyRestore したいとき。状態はプロセスの中だけで、再起動後は last_seq を渡して数え直し、前のプロセスの世代は verifyRestore の対象になりません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・区画が 3 つちょうどでない・2 つの区画が同じ保管先・keep_generations や last_seq の形・port の関数が無いとき INVALID_OPTIONS、操作の入力の形が違うとき INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "スナップショットの結果が分からないとき SNAPSHOT_UNKNOWN（同じ要求を保つ）、送らなかったとき SNAPSHOT_REFUSED、CREATED でない・停止・結果や content_digest の形が違うとき SNAPSHOT_FAILED で、世代にしません。",
          "世代の記録の書込の結果が分からないとき RECORD_UNKNOWN（同じ要求を保つ）、送らなかったとき（同じキーの別の中身を含む）RECORD_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき RECORD_FAILED で、世代として数えません。",
          "verifyRestore で、この部品が記録した世代として無い snapshot_ref のとき UNKNOWN_GENERATION で止めます。",
          "復元の結果が分からないとき RESTORE_UNKNOWN、継手が送らなかった・backend が復元を断ったとき RESTORE_REFUSED、停止・知らない restore_state・DRY_RUN なのに適用したと言ったとき RESTORE_FAILED で止めます。",
          "復元した digest が世代の content_digest と合わないとき（C-02 が ok: true でない、sha256 でない、値が違う）RESTORE_DIGEST_MISMATCH（digest_verified: false）、readRestored が投げた・形が違うとき RESTORED_READ_FAILED、C-02 の関数が投げたとき INTEGRITY_PORT_FAILED（どちらも digest_verified: null）で、確かめにしません。",
          "その区画に結果の分からない送信があるとき CLASS_PENDING、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING、timestamp が実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます。"
        ],
        "what_it_does_not_do": [
          "区画ごとの世代バックアップと復元の検めの部品は継手ではありません。自分の Request/Receipt は無く、スナップショット・模擬の復元・世代の記録の書込という外への効果は、束ねた継手（C-05・C-06・C-07）がそれぞれの契約で起こします。この部品自身は外へ触れません。",
          "束ねた部品の意味（作れた ≠ 中身の正しさ・模擬の復元 ≠ 本番の復元・照合できた ≠ 承認）は、それぞれの部品の側に残ります。",
          "削除・保持・アーカイブの要求は送らず、古い世代を消しません。世代は上書きしません。",
          "結果は承認ではありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "世代の記録が保存されたのに結果が崩れたときは、その seq のキーが backend に残り、次の backup の記録は断られ続けます（fail-closed）。直すのは呼び出し側で、last_seq を合わせます。",
          "本物の storage-lifecycle・storage-integrity の継手と backend には、この部品の試験では接続していません。単体試験とファズは代役で、本物との結線は別の結線の検めで確かめます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のバックアップ製品の雛形ではありません。"
        ],
        "integration": "src/backup.js の createBackup(options) で作り、{ ok, errors, instance } を返します。options は policies { pii, business, evidence }（target・keep_generations・任意の last_seq）・request_prefix・ports・任意の timestamp です。操作は backup・verifyRestore・resolvePending・status で、結果に approval: false が付きます。port は区画ごとの storage.<class>.submit（storage-lifecycle の継手のクライアント）・verifyIntegrity（C-02）・任意の readRestored です。資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-01",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "メール配信（transactional） — メール配信継手（Tsugite）",
        "summary": "メール配信継手（Tsugite）は、確認・領収・リマインドなどのメールを、pin した宛先・検証済みの同意・最小化の記録・本文の digest・宛先偽装の無いヘッダー・冪等キーを検めてからメール backend へ送り、backend が返す受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。一つでも合わなければ送りません（fail-closed）。",
        "problem": "メールを「届いた」で終わらせてしまうこと、つまり宛先を偽ったメールや同意の無いメール、化けたメールを送ってしまうこと、そして送れたことを届いたことと取り違えることを防ぎます。送れた状態（REQUESTED）は到達ではなく、delivered: false として扱います。",
        "use_when": [
          "確認・領収・リマインドなどのメールを、外部のメール backend から送りたいとき。",
          "送り先を、pin した宛先の表（recipient_ref とメールアドレス）に限りたいとき。",
          "検証済みの同意が無い宛先や、bounce・苦情を記録した宛先には送りたくないとき。",
          "同じメールを二重に送りたくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
          "偽の受領が出たら、人が確かめるまで送信を止めたいとき。"
        ],
        "do_not_use_when": [
          "SMS・push・chat で送りたいとき。この継手は email だけで、ほかの channel は範囲外です。",
          "到達の確認（D-05）や、bounce・苦情の判定（D-06）をこの継手にさせたいとき。これらは核の側で、この継手はその結果を受け取るだけです。",
          "Cc・Bcc で複数の相手に送りたいとき。To はちょうど 1 つで pin した宛先と一致する必要があり、Cc・Bcc・Resent-* は送りません。",
          "送信の成功を、到達や承認として扱いたいとき。送れたことは到達ではなく、到達は承認ではありません。同意も Authority ではありません。",
          "すぐに使えるメール backend がほしいとき。実メール backend（SMTP・各社の API）は含まず、どれにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "宛先の偽装を検めます。To が pin した宛先と合わない、To が 2 つある、Cc・Bcc がある、ヘッダーに CR/LF の注入があるメールは送りません。pin していない宛先にも送りません。",
          "同意を検めます。同意の無い送信、検証されていない同意での送信は送りません。",
          "bounce・苦情を記録した宛先には送りません。",
          "本文を検めます。digest と合わない本文（改ざん）や、上限を超える大きさの本文は送りません。",
          "二重送信を検めます。同じ idempotency_key はもう一度送りません（ALREADY_SENT）。",
          "受領（NotificationReceipt）を照合します。request_digest を偽った受領、証拠の無い delivered、UNKNOWN を送信に格上げした受領、side effect の食い違いは受理せず、以後は停止します（HALTED）。",
          "backend の障害は UNKNOWN とし、送信として扱いません。再送は照会（lookup）で解きます。",
          "秘密の渡し方を検めます。SecretRef の隣に生の秘密を置いた要求は拒否します。"
        ],
        "what_it_does_not_do": [
          "宛先の許可・配信の同意・本文の最小化を決めるのは核（policy-facts・consent-facts・redact-facts）の役目です。この継手は、その結果を受け取って検めるだけです。",
          "送れたことは到達ではありません（REQUESTED は delivered: false）。どの結果も承認ではありません（approval: false）。",
          "実メール backend（SMTP・各社の API）を含みません。port の実装と pin する対象は未定で、接続するときに別の関門で決めます。",
          "email 以外の channel（SMS・push・chat）は扱いません。",
          "到達の確認（D-05）と、bounce・苦情の判定（D-06）はしません。その結果を受け取るだけです。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "メール API の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createMailClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。bounce・苦情は recordSuppression で渡します。利用側が用意するもの: メール backend への port（send と、任意で lookup）。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-02",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "SMS 配信 — SMS・push 配信継手（Tsugite）",
        "summary": "SMS・push 配信継手（Tsugite）は、SMS の通知要求（NotificationRequest、channel sms）を、pin した宛先表・検証済みの同意・最小化の記録・冪等キー・本文の digest と上限・SMS の本文の形で検めてから送信基盤へ渡し、戻った受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。SMS を運ぶ継手で、本人確認の機能は含みません。一つでも合わなければ送りません（fail-closed）。",
        "problem": "SMS を送るとき、pin していない番号や形の崩れた番号（E.164 でない番号）へ送ってしまうこと、同意や最小化の記録が無いまま送ること、同じ通知を二度送ること、そして「依頼した」を「届いた」「読まれた」と取り違えることを防ぎます。",
        "use_when": [
          "通知の SMS を、pin した宛先表の番号（E.164）にだけ送りたいとき。",
          "送る前に、検証済みの同意と本文の最小化の記録があることを確かめたいとき（どちらも設定で必須にできます）。",
          "同じ通知を二度送りたくないとき。冪等キーを必須にし、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
          "不達・苦情のあった宛先に、以後は送りたくないとき（recordSuppression）。",
          "依頼した・届いた・読まれた・確認されたを、それぞれの証拠で分けて扱いたいとき。"
        ],
        "do_not_use_when": [
          "本人確認に使いたいとき。この継手は SMS の通知要求を運ぶ継手で、本人確認の機能は含みません。SMS が届いた・読まれたことは、本人を確かめたことではありません。",
          "メール・Webhook の channel で送りたいとき。channel は sms・push だけです。D-01 は mail 継手、D-04 は notify 継手です。",
          "送れたこと・届いたことを、承認や決裁として扱いたいとき。送れた・届いたは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える送信基盤がほしいとき。実の送信基盤（どの SMS のサービスか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "宛先を検めます。pin した宛先表に無い宛先、止めた宛先（不達・苦情）、要求の channel と宛先の channel が合わないもの、email・webhook の channel の要求は送りません。",
          "宛先表の形を検めます。SMS の宛先は E.164（+ と国番号から 7〜15 桁）で、表に 1 件でも形の崩れた宛先（E.164 でない・空白を含む）があれば、表全体を使いません。",
          "SMS の本文を検めます。UTF-8 として正しくない本文、改行のほかの制御文字を含む本文は送りません。",
          "本文の digest の改ざんと、上限（max_payload_bytes）を超える本文を送りません。",
          "同意と最小化を検めます。設定で必須にしたとき、同意が無い・検証されていない要求、最小化の記録が無い要求は送りません。",
          "冪等キーの無い要求は送りません。同じ冪等キーは二度送りません（ALREADY_SENT）。",
          "送信基盤の認証は SecretRef の場所だけを運びます。SecretRef の欄に秘密そのものが入った要求は送りません。",
          "受領（NotificationReceipt）を照合します。証拠の無い到達・既読・確認の状態、message_id の無い送信、side effect の食い違い、request_digest の偽りは受理せず、そのあとは停止します（HALTED）。送信基盤の障害は UNKNOWN とし、送信として扱いません。"
        ],
        "what_it_does_not_do": [
          "本人確認はしません。SMS の通知要求を運ぶ継手で、本人確認の機能は含みません。届いた・読まれたは、本人の確認ではありません。",
          "送れたことは到達ではありません。SENT の delivered は、到達の証拠がある状態のときだけ true です。依頼しただけ（REQUESTED）は届いたではありません。",
          "SMS の文字数（分割・文字コードによる上限）は見ません。上限は bytes だけです。",
          "同意が有効か・最小化が十分かを決めるのは核（consent-facts・redact-facts）の役目で、宛先の許可は policy-facts の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の送信基盤を含みません。どのサービスにつなぐか、port の実装と宛先表は未定で、接続するときに別の関門で決めます。",
          "送れた・届いたは承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（channel sms）でつなぎます。createSmsPushClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 送信基盤への port（send と、任意で lookup）と、cfg の宛先表 recipients。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-03",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "プッシュ通知 — SMS・push 配信継手（Tsugite）",
        "summary": "SMS・push 配信継手（Tsugite）は、push の通知要求（NotificationRequest、channel push）を、pin した宛先表（端末の参照）・検証済みの同意・最小化の記録・冪等キー・本文の digest と上限・push の本文の形で検めてから送信基盤へ渡し、戻った受領（NotificationReceipt）を契約どおりに照合する継手（つぎて）です。push の本文で送り先を差し替えさせず、一つでも合わなければ送りません（fail-closed）。",
        "problem": "push 通知を送るとき、本文に送り先（token・to・topic など）を入れて pin していない端末へ差し替えてしまうこと、同意や最小化の記録が無いまま送ること、同じ通知を二度送ること、そして「依頼した」を「届いた」「読まれた」と取り違えることを防ぎます。",
        "use_when": [
          "push 通知を、pin した宛先表の端末の参照（空白の無い参照）にだけ送りたいとき。",
          "送り先は宛先表だけで決め、本文（JSON）から送り先を変えさせたくないとき。",
          "送る前に、検証済みの同意と本文の最小化の記録があることを確かめたいとき（どちらも設定で必須にできます）。",
          "同じ通知を二度送りたくないとき。冪等キーを必須にし、受領が確定しないまま再送する場合は照会（lookup）で解きます。",
          "不達・苦情のあった宛先に、以後は送りたくないとき（recordSuppression）。",
          "依頼した・届いた・読まれた・確認されたを、それぞれの証拠で分けて扱いたいとき。"
        ],
        "do_not_use_when": [
          "push の本文の中身の意味（通知として正しいか）を確かめたいとき。この継手が止めるのは送り先の差し替えだけです。",
          "メール・Webhook の channel で送りたいとき。channel は sms・push だけです。D-01 は mail 継手、D-04 は notify 継手です。",
          "送れたこと・届いたことを、承認や決裁として扱いたいとき。送れた・届いたは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える送信基盤がほしいとき。実の送信基盤（どの push のサービスか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "宛先を検めます。pin した宛先表に無い宛先、止めた宛先（不達・苦情）、要求の channel と宛先の channel が合わないもの、email・webhook の channel の要求は送りません。",
          "宛先表の形を検めます。push の宛先は空白の無い端末の参照で、表に 1 件でも形の崩れた宛先があれば、表全体を使いません。",
          "push の本文を検めます。UTF-8 の JSON オブジェクトでない本文と、token・to・topic・registration_ids・condition・target を持つ本文（送り先の差し替え）は送りません。",
          "本文の digest の改ざんと、上限（max_payload_bytes）を超える本文を送りません。",
          "同意と最小化を検めます。設定で必須にしたとき、同意が無い・検証されていない要求、最小化の記録が無い要求は送りません。",
          "冪等キーの無い要求は送りません。同じ冪等キーは二度送りません（ALREADY_SENT）。",
          "送信基盤の認証は SecretRef の場所だけを運びます。SecretRef の欄に秘密そのものが入った要求は送りません。",
          "受領（NotificationReceipt）を照合します。証拠の無い到達・既読・確認の状態、message_id の無い送信、side effect の食い違い、request_digest の偽りは受理せず、そのあとは停止します（HALTED）。送信基盤の障害は UNKNOWN とし、送信として扱いません。"
        ],
        "what_it_does_not_do": [
          "送れたことは到達ではありません。SENT の delivered は、到達の証拠がある状態のときだけ true です。依頼しただけ（REQUESTED）は届いたではありません。",
          "push の本文の中身の意味（通知として正しいか）は見ません。止めるのは送り先の差し替えだけです。",
          "同意が有効か・最小化が十分かを決めるのは核（consent-facts・redact-facts）の役目で、宛先の許可は policy-facts の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の送信基盤を含みません。どのサービスにつなぐか、port の実装と宛先表は未定で、接続するときに別の関門で決めます。",
          "送れた・届いたは承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の SMS・push サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（channel push）でつなぎます。createSmsPushClient({ cfg, port }) の send(NotificationRequest, bytes, verifiedConsents) は、SENT・ALREADY_SENT・FAILED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 送信基盤への port（send と、任意で lookup）と、cfg の宛先表 recipients。副作用は取り消せない外部への送信（EXTERNAL_IRREVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-04",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Webhook 配信 — 通知配信継手（Tsugite）",
        "summary": "通知配信継手（Tsugite）は、外部へ Webhook などのイベント通知を送る前に、その通知を送ってよいかだけを判定する継手（つぎて）です。pin した宛先・許可したイベント・冪等キー・署名済みの payload・重複の無さを検め、一つでも欠ければ「送らない」と判定します（fail-closed）。送信・再送・payload の署名はしません。送る前の判定だけです。",
        "problem": "連携の裏配管として外部へ通知を送るとき、pin していない宛先（任意の URL）へ送ってしまうこと（SSRF）、許可していないイベントを送ってしまうこと、同じ通知を二重に送ってしまうこと、署名の無い payload を送ってしまうことを、送る前の判定で防ぎます。",
        "use_when": [
          "自分の送信処理の前に置いて、Webhook などの通知を送ってよいかを判定させたいとき。",
          "送り先を pin した宛先の集合に限り、任意の URL への送出を拒否したいとき（anti-SSRF）。",
          "送ってよいイベントの種別を、許可した一覧（allowlist）に限りたいとき。",
          "もう送った冪等キーの集合を自分で持っていて、同じ通知の二重送信を送る前に止めたいとき。"
        ],
        "do_not_use_when": [
          "Webhook を実際に送る部品、失敗したときに再送する部品、payload に署名する部品がほしいとき。この継手はどれもしません。送る前の判定だけです。",
          "送った通知が届いたかを、受領（receipt）で確かめたいとき。到達の receipt の検めは含みません。",
          "payload の署名そのものを検めたいとき。署名済みかどうかは、呼び出し側が渡す真偽値（payload_signed）で判定します。",
          "冪等キーや署名の要求を外したまま使うとき。require_idempotency_key・require_signed_payload を false にすると、その検めが外れます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "宛先を検めます。pin した宛先の集合の外への通知は、送らないと判定します（任意の URL への送出を拒否。SSRF）。",
          "イベントを検めます。許可していないイベント種別の通知は、送らないと判定します。",
          "冪等キーを検めます。冪等キーの無い通知は、送らないと判定します（require_idempotency_key が既定の true のとき）。",
          "署名を検めます。payload_signed が真でない（未署名の）通知は、送らないと判定します（require_signed_payload が既定の true のとき）。",
          "重複を検めます。渡された「もう送った冪等キーの集合」にあるキーの通知は、二重送信として送らないと判定します。"
        ],
        "what_it_does_not_do": [
          "Webhook を送信しません。失敗したときの再送もしません。payload に署名もしません。これらは利用側が行います。",
          "到達の受領（receipt）を検めません。この継手がするのは送る前の判定だけです。",
          "冪等キーと署名の要求は設定で外せます。require_idempotency_key・require_signed_payload を false にすると、その検めが外れ、冪等キーの欠落・未署名を理由には止めなくなります（どちらも既定は true）。",
          "もう送った冪等キーの集合（delivered）を保持しません。呼び出し側が渡します。",
          "実際の配信 API と署名の方式は決めていません。配備するときに config で決めます。",
          "配信の同意や承認の経路、配信の来歴は核（consent-facts・provenance-facts）の側です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "通知 API の使い方の雛形ではありません。"
        ],
        "integration": "入口は src/guards.js の evaluate(cfg, request, delivered)（純粋な関数。送る前の判定）です。request に { recipient, event_type, idempotency_key, payload_signed }、delivered にもう送った冪等キーの集合 { key: 1 } を渡すと、送ってよければ { ok: true, errors: [] }、だめなら { ok: false, errors: [...] } を返します。cfg は allowed_recipients・allowed_events・require_idempotency_key・require_signed_payload。port は無く、送信・再送・署名・receipt の検めは利用側が用意します。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-05",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "配信到達・開封確認 — メッセージングの拡張継手（Tsugite）",
        "summary": "メッセージングの拡張継手（Tsugite）は、配信の証拠の問い合わせ（DeliveryEvidence の要求）を契約の型で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。依頼した・届いた・読まれた・確認されたの各段を別の証拠として扱い、届いたことを業務の受け入れと取り違えません（fail-closed）。",
        "problem": "届いた・読まれたを事実として残すとき、問い合わせた段とは別の段の証拠を受け取ってしまうこと、そして配信の証拠から業務の受け入れを導いてしまうことを防ぎます。",
        "use_when": [
          "届いた・読まれた・確認されたを、段ごとの証拠として基盤に問い合わせ、照合してから記録したいとき。",
          "上の段の確認から、下の段の確認や業務の受け入れを推し量らせたくないとき。",
          "同意や監査のために、依頼しただけの状態と届いた状態を分けて残したいとき。",
          "受領に不整合が出たら、そこで止めたいとき（HALTED）。",
          "同じ問い合わせを二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "受け取った側が処理したか（業務として受け入れたか）を確かめたいとき。この継手は確かめません。",
          "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
          "通知そのものを送りたいとき。送信は D-01 の mail 継手、D-02・D-03 の SMS と push の継手、D-04 の notify 継手が担います。",
          "届いたことを、承認や決裁として扱いたいとき。届いたは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の基盤（どの配信の証拠の仕組みか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "問い合わせた段（evidence_kind）の証拠だけを受理します。別の段の証拠を返す受領は受理せず、停止します。",
          "業務の受け入れを名乗る受領（配信の証拠から業務の受け入れを導くもの）は受理せず、停止します。",
          "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
          "side effect を検めます。配信の証拠の問い合わせは side effect 無し（NONE）です。外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
          "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "配信の証拠を自ら作る仕組みは含みません。配信の証拠の問い合わせを基盤へ運び、戻った受領を照合するだけです。どの配信の証拠の仕組みにつなぐかは未定で、接続するときに別の関門で決めます。",
          "届いた・読まれた・確認されたは、業務の受け入れではありません。受け取った側が処理したかは確かめません。",
          "業務の受け入れの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "届いたは承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に DeliveryEvidence の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には evidence_kind・evidence_state・confirmed・business_acceptance が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は 無し（NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-06",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "バウンス・苦情の記録 — メッセージングの拡張継手（Tsugite）",
        "summary": "メッセージングの拡張継手（Tsugite）は、不達（バウンス）と苦情の記録（BounceComplaint の要求）を契約の型で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合して、止めたか（suppression_state・suppressed）を返す継手（つぎて）です。一時的な保留（DEFERRAL）を止める理由として扱わず、一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "不達・スパム申告（苦情）を記録するとき、一時的な保留まで宛先を止める理由にしてしまうこと、効果が不明な受領や別の要求の受領を受け取ってしまうこと、同じ記録を二度送ることを防ぎます。",
        "use_when": [
          "不達・スパム申告（苦情）を基盤に記録し、止めた状態（suppression_state・suppressed）を受け取りたいとき。",
          "一時的な保留（DEFERRAL）では宛先を止めたくないとき。",
          "止めた宛先を、利用側で送信の継手（mail・SMS と push）へ渡したいとき。この継手は、渡すための状態を返します。",
          "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
        ],
        "do_not_use_when": [
          "送信の停止まで任せたいとき。この継手は状態を返すだけで、止めた宛先を送信の継手（mail・SMS と push）へ渡すのは利用側の役目です。",
          "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
          "止めたことを、承認や決裁として扱いたいとき。どの結果も承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の基盤（どのキュー・発行購読・配信の証拠の仕組みか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "一時的な保留（DEFERRAL）で宛先を止めようとする記録は拒否します。DEFERRAL と、止めない記録（NOT_SUPPRESSED）の side effect は無し（NONE）です。",
          "side effect を検めます。不達・苦情の記録の side effect は NONE か EXTERNAL_REVERSIBLE です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
          "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
          "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "止めた宛先を送信の継手（mail・SMS と push）へ渡しません。この継手は状態を返すだけです。",
          "不達・苦情の記録を運ぶ継手で、苦情の中身の判断や送信の評価（レピュテーション）の管理の機能は含みません。",
          "止める・再送するの方針を決めるのは核（policy-facts）と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "どの結果も承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に BounceComplaint の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には event・suppression_state・suppressed が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は NONE か EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-07",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "メッセージキュー — メッセージングの拡張継手（Tsugite）",
        "summary": "メッセージングの拡張継手（Tsugite）は、キューへの出し入れ（Queue の要求。ENQUEUE・DEQUEUE・ACK）を、pin したキューと冪等キーで検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。二重に積まず、積んだことを届いたことと取り違えません。一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "非同期の受け渡しにキューを使うとき、pin していないキューへ積むこと、同じメッセージを二重に積むこと、同じ冪等キーで別の中身を積むこと、そして積んだことを受け取った側の処理と取り違えることを防ぎます。",
        "use_when": [
          "非同期の受け渡しで、pin したキューにだけ積み（ENQUEUE）・取り出し（DEQUEUE）・ACK を運びたいとき。",
          "同じメッセージを二重に積みたくないとき。ENQUEUE には idempotency_key を必須にし、重複（DEDUP_HIT）は新しい効果なしとして扱います。",
          "受領の状態が操作（op）に対応しているかを確かめてから受け取りたいとき。",
          "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
        ],
        "do_not_use_when": [
          "取り出した側が処理したか（業務として受け入れたか）を確かめたいとき。この継手は確かめません。",
          "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
          "積んだことを、承認や決裁として扱いたいとき。積んだは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の基盤（どのキューの仕組みか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "pin していないキューへの要求は送りません。",
          "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
          "idempotency_key の無い ENQUEUE は送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。",
          "重複（DEDUP_HIT）なのに新しい効果が付いた受領と、DEQUEUE への DEDUP_HIT は受理しません。",
          "操作に対応しない状態の受領は受理しません（ENQUEUE→ENQUEUED か DEDUP_HIT、DEQUEUE→DEQUEUED、ACK→ACKED、または FAILED だけ）。",
          "side effect を検めます。キューの操作は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
          "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "キューそのものは含みません。キューへの出し入れの要求を運ぶ継手です。どの基盤につなぐか、port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "積んだこと・取り出したことは、受け取った側が処理したこと（業務として受け入れたこと）ではありません。確かめません。",
          "重複の検出や、止める・再送するの方針を決めるのは核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "積んだは承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に Queue の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には queue_state が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-08",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Pub/Sub ファンアウト — メッセージングの拡張継手（Tsugite）",
        "summary": "メッセージングの拡張継手（Tsugite）は、発行と購読（PubSub の要求。PUBLISH・SUBSCRIBE・UNSUBSCRIBE）を、pin した話題（topic）とイベントのスキーマで検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。発行したことを受け取られたことと取り違えません（received: NOT_OBSERVED）。一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "1 対多の配信に発行と購読を使うとき、pin していない話題への発行・購読、スキーマを pin していないイベントの発行、そして発行したことを購読側が受け取ったことと取り違えることを防ぎます。",
        "use_when": [
          "pin した話題にだけ、発行（PUBLISH）・購読（SUBSCRIBE）・購読の解除（UNSUBSCRIBE）を運びたいとき。",
          "発行するイベントに、pin したイベントのスキーマ（event_schema_ref）を必ず付けたいとき。",
          "発行したことと、受け取られたことを分けて扱いたいとき。",
          "同じ発行を二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。",
          "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
        ],
        "do_not_use_when": [
          "購読側が受け取ったか・処理したかを確かめたいとき。received は観測しておらず（NOT_OBSERVED）、業務として受け入れたかも確かめません。",
          "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
          "発行したことを、承認や決裁として扱いたいとき。発行したは承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の基盤（どの発行購読の仕組みか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "pin していない話題、pin していないイベントのスキーマへの要求は送りません。",
          "イベントのスキーマ（event_schema_ref）の無い PUBLISH は送りません。",
          "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
          "操作に対応しない状態の受領は受理しません（PUBLISH→PUBLISHED、SUBSCRIBE→SUBSCRIBED、UNSUBSCRIBE→UNSUBSCRIBED、または FAILED だけ）。",
          "side effect を検めます。発行と購読は EXTERNAL_REVERSIBLE（FAILED は NONE）です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
          "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "発行と購読の要求を基盤へ運ぶ継手で、1 対多に配る（ファンアウト）機能そのものは含みません。",
          "発行したことは、受け取られたことではありません（received: NOT_OBSERVED）。",
          "実の基盤を含みません。どの発行購読の仕組みにつなぐか、port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "業務の受け入れの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "発行したは承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に PubSub の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には pubsub_state・received: NOT_OBSERVED が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "D-09",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "デッドレター処理 — メッセージングの拡張継手（Tsugite）",
        "summary": "メッセージングの拡張継手（Tsugite）は、デッドレターの操作（DeadLetter の要求。PARK・REDRIVE・PURGE）を、pin したデッドレター（dlq_refs）で検めてから基盤へ運び、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。取り消せない消去（PURGE）は設定 allow_purge: true のときだけ運び、一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "失敗したメッセージの退避にデッドレターを使うとき、pin していないデッドレターを操作すること、設定で許していないのに取り消せない消去（PURGE）をしてしまうこと、そして効果が意図より大きい受領を受け取ってしまうことを防ぎます。",
        "use_when": [
          "失敗したメッセージの退避のために、pin したデッドレターへの PARK・REDRIVE を運びたいとき。",
          "取り消せない消去（PURGE）を、設定で明示して許したときだけ運びたいとき（allow_purge の既定は false）。",
          "同じ操作を二度送りたくないとき。受領が確定しないまま再送する場合は照会（lookup）で解きます。",
          "受領に不整合が出たら、そこで止めたいとき（HALTED）。"
        ],
        "do_not_use_when": [
          "消してよいかの判断を任せたいとき。消してよいかの判断は核と人の役目です。",
          "メッセージの本文を載せたいとき。この継手はメッセージの中身を扱わず、参照だけを運びます。",
          "退避・消去の成功を、承認や決裁として扱いたいとき。どの結果も承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の基盤（どのデッドレターの仕組みか）は含まず、まだ決まっていません。"
        ],
        "what_it_checks": [
          "pin していないデッドレターへの要求は送りません。",
          "設定 allow_purge: true でないときの PURGE は送りません。",
          "参照を検めます。空の参照、パス（.. を含む）の参照、追加のフィールドは拒否します。空の一覧は何も許しません。",
          "操作に対応しない状態の受領は受理しません（PARK→PARKED、REDRIVE→REDRIVEN、PURGE→PURGED、または FAILED だけ）。",
          "side effect を検めます。PARK・REDRIVE は EXTERNAL_REVERSIBLE、PURGE は EXTERNAL_IRREVERSIBLE、FAILED は NONE です。意図より外への効果を増やす受領と、効果が不明（UNKNOWN）な受領は受理しません。",
          "別の要求の受領（request_digest の偽り）は受理しません。照合できない受領のあとは停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "PURGE で消したものは取り消せません（EXTERNAL_IRREVERSIBLE）。この継手は、設定で許したかを検めるだけです。",
          "デッドレターの操作の要求を運ぶ継手で、失敗したメッセージを自ら見つけて退避する機能は含みません。",
          "消してよいかの判断は核と人の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の基盤を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "どの結果も承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のキューやメッセージ基盤の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/messaging-ext/v1 でつなぎます。createMessagingExtClient({ cfg, port }) の submit(request) に DeadLetter の要求 を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。DONE の result には dlq_state が付きます。利用側が用意するもの: 基盤への port（submit と、任意で lookup）と、cfg で pin する一覧。副作用は PARK・REDRIVE が EXTERNAL_REVERSIBLE、PURGE が EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-01",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "定時トリガ（cron） — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。定時トリガ（cron）としては、予定（ScheduleTrigger）の登録・更新・停止の要求を検めて実行基盤のスケジューラへ送り、受領の予定の状態と次の起動を照合します。起動そのものは実行基盤が行います。",
        "problem": "毎日・毎時の起動を外部のスケジューラに任せるとき、pin していない予定や起動の指定を登録してしまうこと、止めた予定に次の起動が残っていると読み違えること、受領を照合しないまま「登録できた」と扱うことを防ぎます。登録できたことを業務の許可と取り違えないための検めです。",
        "use_when": [
          "毎日・毎時の起動の予定を外部のスケジューラに登録・更新・停止するとき、要求を pin した予定（schedule_refs）と起動の指定（trigger_spec_refs）で検めてから送りたいとき。",
          "スケジューラの受領をそのまま信じず、受領の状態が要求（op）に対応するもの（REGISTER→REGISTERED、UPDATE→UPDATED、DISABLE→DISABLED）か、効果のない FAILED かを照合してから扱いたいとき。",
          "同じ要求を二度送りたくないとき。結果が分からなかった送信は照会（lookup）で解きます。",
          "受領が照合できないとき、以後の送信を止めたいとき（HALTED）。"
        ],
        "do_not_use_when": [
          "予定どおりに起動したかを確かめたいとき。この継手が見るのは登録の受領だけで、起動したかどうかは見ません。",
          "スケジューラそのものがほしいとき。実の実行基盤は含まず、どのスケジューラにつなぐかはまだ決まっていません。",
          "予定を登録できたことを、その仕事を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していない予定・起動の指定、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "受領の状態は、要求（op）に対応するもの（REGISTER→REGISTERED、UPDATE→UPDATED、DISABLE→DISABLED）か、効果のない FAILED（副作用なし・次の起動なし）だけを受け取ります。それ以外は受理しません。",
          "止めた・失敗した予定に次の起動（next_fire_ref）が付いた受領を受理しません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "予定どおりに起動したかは見ません（登録の受領だけを見ます）。起動そのものは実行基盤が行います。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に ScheduleTrigger の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に schedule_state・next_fire_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の schedule_refs・trigger_spec_refs。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-02",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "遅延ジョブ — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。遅延ジョブとしては、N 分後に実行する仕事（DelayedJob）の要求を pin したジョブで検めて実行基盤へ送り、受領の積み込みの状態（enqueue_state）と worker の割当て（worker_assignment_ref）を照合します。時間を待って実行するのは実行基盤です。",
        "problem": "リマインドのように後で実行する仕事を外部の実行基盤に預けるとき、pin していないジョブを積むこと、同じ仕事を二重に積むこと、失敗した積み込みに worker が割り当たったと読み違えることを防ぎます。積めたことを業務の許可と取り違えないための検めです。",
        "use_when": [
          "N 分後に実行する仕事を外部の実行基盤に預けるとき、pin したジョブ（job_refs）だけを送りたいとき。",
          "同じ仕事を二重に積みたくないとき。重複（DEDUP_HIT）は新しい効果なしとして受け取り、同じ冪等キーで別の中身は送りません。",
          "実行基盤の受領をそのまま信じず、契約どおりに照合してから積めたとしたいとき。",
          "結果が分からなかった送信を、二度送らずに照会（lookup）で解きたいとき。"
        ],
        "do_not_use_when": [
          "積んだ仕事が予定の時刻に実行されたかを確かめたいとき。この継手が見るのは積み込みの受領だけで、実行されたかは見ません。",
          "時間を待って実行する仕組みそのものがほしいとき。実の実行基盤は含まず、どの仕組みにつなぐかはまだ決まっていません。",
          "積めたことを、その仕事を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していないジョブ、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "同じ冪等キーで別の中身の要求は送りません。重複（DEDUP_HIT）に新しい効果が付いた受領は受理しません。",
          "失敗した積み込みに worker の割当て（worker_assignment_ref）が付いた受領を受理しません。",
          "受領の状態が要求に対応しないとき、受理しません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "積んだ仕事が実行されたかは見ません（積み込みの受領だけを見ます）。時間を待って実行するのは実行基盤です。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に DelayedJob の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に enqueue_state・worker_assignment_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の job_refs。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-03",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "タスクキュー・ワーカー — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。タスクキュー・ワーカーとしては、重い処理のタスク（TaskEnqueue）の要求と中身の bytes を、pin したキューと中身の digest・上限で検めて実行基盤のキューへ送り、受領の積み込みの状態と worker の割当てを照合します。タスクを実行するのは実行基盤です。",
        "problem": "重い処理を後ろで回すためにキューへ積むとき、途中で改ざんされた中身や大きすぎる中身を積むこと、同じタスクを二重に積むこと、失敗した積み込みに worker が割り当たったと読み違えることを防ぎます。",
        "use_when": [
          "重い処理を後ろで回すため、pin したキュー（queue_refs）にだけタスクを積みたいとき。",
          "キューに積む中身（bytes）の digest と上限（max_payload_bytes）を、送る前に自分で確かめたいとき。",
          "同じタスクを二重に積みたくないとき。重複（DEDUP_HIT）は新しい効果なしとして受け取り、同じ冪等キーで別の中身は送りません。",
          "結果が分からなかった送信を、二度送らずに照会（lookup）で解きたいとき。"
        ],
        "do_not_use_when": [
          "積んだタスクが実行されたかを確かめたいとき。この継手が見るのは積み込みの受領だけで、実行されたかは見ません。",
          "キューの仕組みそのものがほしいとき。実の実行基盤は含まず、どのキューにつなぐかはまだ決まっていません。",
          "積めたことを、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していないキュー、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "中身を検めます。要求の payload_digest（対象は task_ref）と中身の bytes が合わないとき（改ざん）は送りません。TaskEnqueue は bytes が必須です。",
          "上限（max_payload_bytes）を超える中身は送りません。",
          "同じ冪等キーで別の中身の要求は送りません。重複（DEDUP_HIT）に新しい効果が付いた受領は受理しません。",
          "失敗した積み込みに worker の割当て（worker_assignment_ref）が付いた受領を受理しません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "積んだタスクが実行されたかは見ません（積み込みの受領だけを見ます）。タスクを実行するのは実行基盤です。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request, bytes) に TaskEnqueue の要求と bytes（必須）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に enqueue_state・worker_assignment_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の queue_refs・max_payload_bytes（必須）。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-04",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "ワークフロー統括（saga） — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。ワークフロー統括（saga）としては、サガの手順（SagaStep）の前進（FORWARD）と補償（COMPENSATE）の要求を、pin したサガと手順で検めて実行基盤へ送り、受領の手順の状態を照合します。サガの調停は実行基盤が行い、補償できたことを人の取消しの承認と取り違えません。",
        "problem": "多段処理が途中で失敗して巻き戻すとき、補償の参照が無い補償を送ること、効果の分からない手順や取り消せない手順を許しなく送ること、補償できたことを人の取消しの承認として扱ってしまうことを防ぎます。",
        "use_when": [
          "多段処理の手順を、pin したサガ（saga_refs）と手順（step_refs。補償の手順も含む）だけで送りたいとき。",
          "途中失敗の巻き戻しで、補償の要求（COMPENSATE）を補償の手順の参照つきでだけ送りたいとき。",
          "受領の手順の状態が要求に対応するか（FORWARD→EXECUTED・FAILED・SKIPPED、COMPENSATE→COMPENSATED・FAILED・SKIPPED）を照合したいとき。",
          "取り消せない効果の手順を、設定で明示したとき（allow_irreversible: true）だけ送りたいとき。既定は false です。"
        ],
        "do_not_use_when": [
          "補償できたことを、人の取消しの承認として扱いたいとき。承認は核と人の役目で、受領からは NOT_DERIVED だけを受け取ります。",
          "手順を実行できたことを、業務の許可として扱いたいとき。業務の許可を導く受領は受理しません。",
          "サガの調停（多段処理の状態機械）そのものがほしいとき。調停は実行基盤の側で、実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していないサガや手順、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "補償の手順の参照が無い補償（COMPENSATE）は送りません。",
          "意図した効果が UNKNOWN の手順は送りません。取り消せない効果の手順は、設定 allow_irreversible: true のときだけ送ります。",
          "受領の手順の状態が要求に対応しないとき、受理しません。",
          "補償の承認や業務の許可を導く受領を受理しません（NOT_DERIVED だけを受け取ります）。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "サガの調停（多段処理の状態機械）は実行基盤の役目で、この継手は持ちません。巻き戻しそのものも行わず、補償の要求を検めて送り、受領を照合するだけです。",
          "補償できても人の取消しの承認ではありません。成功はどれも承認ではなく、どの結果にも approval: false が付きます。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に SagaStep の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に step_state・compensation_authorization・business_authorization を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の saga_refs・step_refs・allow_irreversible。副作用は手順ごとの意図どおり（SKIPPED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-05",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "分散ロック — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。分散ロックとしては、ロック（Lock）の取得（ACQUIRE）・更新（RENEW）・解放（RELEASE）の要求を、pin したロックとこの継手の holder（holder_ref）と上限以内の期限で検めて実行基盤へ送り、受領のロックの状態を照合します。排他そのものは実行基盤の役目で、リーダー選出はしません。",
        "problem": "同時実行の排他を外部のロックの仕組みに任せるとき、他の holder のロックに触ること、期限の無いロックや上限を超える期限のロックを取ること、結果が分からない（UNKNOWN）ときに「持っている」と読み違えることを防ぎます。持っていないロックを持っていると扱わないための検めです。",
        "use_when": [
          "同時実行の排他を外部のロックの仕組みに任せるとき、pin したロック（lock_refs）を、この継手の holder（holder_ref）だけで扱いたいとき。",
          "ロックに期限を付け、期限の上限（max_lock_ttl_seconds）を超えない ACQUIRE・RENEW だけを送りたいとき。",
          "「持っている」（held: true）を、受領の状態が ACQUIRED・HELD のときだけにしたいとき。UNKNOWN からは導きません。",
          "fencing token（fencing_token_ref）を、持っているロック（ACQUIRED・HELD）の受領でだけ受け取りたいとき。"
        ],
        "do_not_use_when": [
          "この継手でリーダー選出（E-06）をしたいとき。この継手の契約にリーダー選出の種類は無く、選出の任期や他の持ち主の観測も持ちません。E-06 は、この継手の分散ロックを束ねる部品(Component)（リーダー選出の部品）が担います。",
          "排他が正しく守られることを、この継手に頼りたいとき。ロックの排他の正しさは実行基盤の役目で、この継手は holder と期限と受領の整合だけを見ます。",
          "ロックの仕組みそのものがほしいとき。実の実行基盤は含まず、どのロックの仕組みにつなぐかはまだ決まっていません。",
          "ロックを持てたことを、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していないロック、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "この継手の holder ではない holder のロックの要求は送りません。他が持っていれば DENIED で、効果なしとして受け取ります。",
          "期限の無い ACQUIRE・RENEW と、上限（max_lock_ttl_seconds）を超える期限の要求は送りません。",
          "受領の状態が要求に対応しないとき（ACQUIRE→ACQUIRED・HELD・DENIED・UNKNOWN、RENEW→HELD・EXPIRED・DENIED・UNKNOWN、RELEASE→RELEASED・EXPIRED・DENIED・UNKNOWN 以外）、受理しません。",
          "解放したロックなど、持っていないロックに fencing token が付いた受領を受理しません。UNKNOWN から「持っている」を導きません。",
          "リーダー選出（E-06）の種類の要求は、この継手の契約に種類が無いので受け付けません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と効果が分からない（UNKNOWN）受領を受理せず、照合できない受領で停止します（HALTED）。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "リーダー選出はしません。E-06 は、この継手を束ねる部品(Component)（リーダー選出の部品）が担います。この継手自身には選出の種類はありません。",
          "ロックの排他の正しさは実行基盤の役目です。この継手は holder と期限と受領の整合だけを見ます。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に Lock の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に lock_state・held・fencing_token_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の lock_refs・holder_ref・max_lock_ttl_seconds。副作用は EXTERNAL_REVERSIBLE（重複・拒否・失敗は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-06",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Component",
        "title": "リーダー選出 — リーダー選出の部品（Component）",
        "summary": "リーダー選出の部品（Component）は、冗長な構成で 1 つだけを動かすために、E-05 分散ロックの継手を束ねてリーダーを選ぶ部品です。立候補・期限の前の更新・降りるを行い、ロックを握れて fencing token があり、リースの期限（送る前の時刻 + ttl − 安全余裕）より前のときだけ「リーダー」と答えます。拒否・期限切れ・結果不明・障害・時計の逆行では、すぐリーダーでなくなります（fail-closed）。リーダーであることは権限ではありません。",
        "problem": "可用性の裏で、同じロックを取り合う 2 つの参加者が同時にリーダーを名乗ること（ロックの基盤が正しいとき）、受領の遅れの分だけ期限を長く見てしまうこと、fencing token の無いままリーダーを名乗ることを防ぎます。",
        "use_when": [
          "冗長な構成で、処理を 1 つの参加者だけに動かしたいとき。E-05 分散ロックの継手のクライアント（submit(LockRequest) を持つもの）を lock として注入します。",
          "立候補（campaign）・期限の前の更新（renew）・降りる（resign）・今リーダーかの確認（status）を、例外を投げない操作で行いたいとき。",
          "リーダーのときに fencing token（fencing_token_ref）を受け取り、書込み先に渡したいとき。leader が true のときは必ず fencing_token_ref があります。",
          "期限を、ロックを頼む前に読んだ時刻 + ttl − 安全余裕で短く見積もりたいとき。時刻は注入した単調な時計（clock.now()）だけから取ります。",
          "降りたら、ロックの解放の結果によらず、手元ではすぐリーダーでないことにしたいとき。"
        ],
        "do_not_use_when": [
          "リーダーであることを、権限や承認として使いたいとき。どの結果にも approval: false と authority: NOT_DERIVED が付きます。",
          "古いリーダーの書込みを、この部品で止めたいとき。fencing token で古いリーダーの書込みを止めるのは書込み先の役目で、この部品は token を渡すだけです。",
          "「リーダー」を、ロックの基盤の最新の状態そのものとして扱いたいとき。「リーダー」はこの参加者の手元の判定です。",
          "再起動をまたいでリーダーを続けたいとき。状態はこのプロセスの中だけで、再起動の後は立候補し直すまでリーダーでありません。",
          "本物のロックの基盤で確かめた部品が要るとき。本物の基盤には一度も接続しておらず、代役のロックだけで確かめました。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違うとき（ttl が 1〜3600 の外、安全余裕が ttl 以上、submit の無い lock、now の無い clock、.. を含む参照）、INVALID_OPTIONS で止めます。",
          "前の操作が終わっていないとき、2 つ目を BUSY で止めます（同時に 1 つの操作だけ）。",
          "リーダーでないのに更新しようとしたとき NOT_LEADER、期限を過ぎてからの更新のとき LEASE_EXPIRED で、ロックへ送りません。ロックが基盤の側で切れていたときも LEASE_EXPIRED です。",
          "ほかの参加者がロックを握っているとき LOCK_DENIED、結果が DONE でないとき（UNKNOWN・REFUSED・ALREADY_DONE・HALTED など）や held が true でないとき LOCK_UNKNOWN で、リーダーでないとします。",
          "握れたのに fencing token が無いとき NO_FENCING_TOKEN、ロックのクライアントが例外を投げたとき PORT_FAILED で、リーダーを名乗りません。",
          "時計が戻ったとき、リースを捨てて CLOCK_WENT_BACKWARDS で止めます。",
          "期限を延ばすのは、status が DONE で held が true、かつ fencing token があるときだけです。同じロックを取り合う 2 つの参加者が同時にリーダーを名乗らないことを、ファズで毎回確かめました。"
        ],
        "what_it_does_not_do": [
          "リーダー選出の部品は継手ではありません。自分の Request/Receipt は無く、ロックの取得・更新・解放という外への効果は、束ねた E-05 の継手が自分の契約（workflow-jobs/v1）で起こします。この部品自身は外へ触れません。",
          "束ねた E-05 の継手の意味（受領の照合・承認ではない）は、継手の側に残ります。",
          "リーダーであることは権限でも承認でもありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "fencing token を使った書込みの守りはしません。古いリーダーの書込みを止めるのは書込み先の役目です。",
          "ロックの基盤の正しさ（線形化・時計のずれ・分断のときの振る舞い）は確かめません。安全性は、その正しさと、呼び出し側が決める安全余裕の見積もりに頼ります。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の調整サービスの使い方の雛形ではありません。"
        ],
        "integration": "src/leader.js の createLeaderElection(options) で作ります。受け取るのは lock_ref・holder_ref・ttl_seconds・renew_margin_ms・lock・任意の clock と timestamp で、{ ok, errors, instance } を返します。campaign・renew・resign（Promise）と status は { ok, errors, leader, fencing_token_ref, approval: false, … } を返します。利用側が用意する port は lock.submit(request)（E-05 の継手のクライアント）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-07",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "デバウンス・スロットル — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。デバウンス・スロットルとしては、間引き（Throttle）の要求を pin した間引きの方針で検めて実行基盤へ送り、受領の判定（decision・allowed・retry_after_ref）を照合します。間引きの判定そのものは実行基盤が行います。",
        "problem": "連打や多重起動を抑える判定を外部の実行基盤に任せるとき、pin していない方針で問い合わせること、許可（ALLOW）の判定に再試行の時刻（retry_after_ref）が付いた食い違った受領を受け取ること、受領を照合しないまま判定を使うことを防ぎます。",
        "use_when": [
          "連打・多重起動の抑制の判定を、pin した間引きの方針（throttle_policies）でだけ実行基盤に問い合わせたいとき。",
          "実行基盤の判定をそのまま信じず、要求と受領の一致（request_id・request_digest）を照合してから使いたいとき。",
          "間引きの問い合わせを、外への効果なし（NONE）として扱いたいとき。効果の付いた受領は受理しません。"
        ],
        "do_not_use_when": [
          "間引きの判定そのものをこの継手に期待するとき。判定は実行基盤の役目で、実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
          "デバウンス専用の要求の種類を求めるとき。契約の種類は間引き（Throttle）で、デバウンス専用の種類はありません。",
          "判定の許可（allowed）を、その処理を実行してよいという業務の許可として扱いたいとき。何をいつ実行してよいかの決定は核（policy-facts）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していない間引きの方針、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "許可（ALLOW）の判定に再試行の時刻（retry_after_ref）が付いた受領を受理しません。",
          "受領の状態が要求に対応しないとき、受理しません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "間引きの判定はしません。判定は実行基盤が行い、この継手は要求を検めて送り、受領の判定を照合するだけです。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "積めた・実行できた・完了したは承認ではありません。どの結果にも approval: false が付きます。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に Throttle の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に decision・allowed・retry_after_ref を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup）と cfg の throttle_policies。副作用は無し（NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-08",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "ジョブ状態照会 — 仕事の制御継手（Tsugite）",
        "summary": "仕事の制御継手（Tsugite）は、実行基盤への要求（Request）を pin した一覧で検めてから送り、戻った受領（receipt）を契約どおりに照合する継手（つぎて）です。ジョブ状態照会としては、ジョブ状態（JobStatus）の照会を pin したジョブで検めて実行基盤へ送り、受領の状態（status）・再試行できるか（retryable）・失敗の参照（failure_ref）を照合します。完了を業務の受け入れと、再試行できるを再試行の許可と取り違えません。",
        "problem": "「終わった?」に答えるためにジョブの状態を実行基盤に尋ねるとき、完了を業務の受け入れと、再試行できるを再試行の許可と取り違えること、たどれない失敗の参照を受け取ることを防ぎます。",
        "use_when": [
          "進捗・完了の確認を、pin したジョブ（job_refs）でだけ実行基盤に問い合わせたいとき。",
          "失敗の参照（failure_ref）を、ai-execution-error の記録へ解決できるときだけ受け取りたいとき。",
          "完了や再試行できるを、業務の受け入れ・再試行の許可と分けて扱いたいとき。受領からは NOT_DERIVED だけを受け取ります。",
          "照会を外への効果なし（NONE）として扱いたいとき。効果の付いた受領は受理しません。"
        ],
        "do_not_use_when": [
          "失敗の記録の中身を検めたいとき。failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけを見ます）。",
          "完了を業務の受け入れとして、再試行できるを再試行の許可として扱いたいとき。どちらも核と人の役目です。",
          "ジョブの状態を持つ仕組みそのものがほしいとき。実の実行基盤は含まず、どれにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "参照を検めます。pin していないジョブ、空の参照、パスや .. を含む参照は送りません。空の一覧は何も許しません。",
          "failure_ref が解決できないとき、または解決の手段（port.resolveFailureRef）が無いときは受領を受理せず、以後は停止します（HALTED）。",
          "failure_ref は FAILED・CANCELLED のときだけ受け取ります。",
          "再試行の許可や業務の受け入れを導く受領を受理しません（NOT_DERIVED だけを受け取ります）。",
          "受領の状態が要求に対応しないとき、受理しません。UNKNOWN の状態から実行や許可を導きません。",
          "受領の request_id・request_digest・参照が要求と一致しないとき（request_digest の偽り）、受理しません。",
          "外への効果を増やす受領と、効果が分からない（UNKNOWN）受領を受理しません。効果は、どこまで及ぶかと取り消せるかの両方で、要求の意図以下でなければなりません。",
          "受領が照合できなければ停止し（HALTED）、以後は送りません。同じ request_id は二度送らず、実行基盤の障害は UNKNOWN として照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけを見ます）。",
          "完了は業務の受け入れではなく、再試行できるは再試行の許可ではありません。成功はどれも承認ではなく、どの結果にも approval: false が付きます。",
          "何をいつ実行してよいかの決定（policy-facts）と重複の検出（reconcile-facts）は核の役目です。業務の許可・再試行の許可・補償の承認は核と人の役目で、この継手がするのは送る前と受け取った後の検めだけです。",
          "実の実行基盤（どのスケジューラ・キュー・ロックの仕組みか）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラやキューの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/workflow-jobs/v1 でつなぎます。createWorkflowClient({ cfg, port }) の submit(request) に JobStatus の要求を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します。DONE に status・retryable・failure_ref・retry_authorization・business_acceptance を添えます。利用側が用意するもの: 実行基盤への port（submit、任意で lookup、failure_ref があるとき必須の resolveFailureRef）と cfg の job_refs。副作用は無し（NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-09",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Component",
        "title": "読み取り専用の集計の実行 — 読み取り専用の集計の実行の部品（Component）",
        "summary": "読み取り専用の集計の実行の部品（Component）は、入力を検めて読み、集計の結果を副本として書く部品です。入力はどれも期待する sha256 を添えて C-01 の継手で読み、digest が合ったものだけを JSON として読みます。集計器（fn）には凍った写しを渡します。fn の出力は RFC 8785 の bytes にし、系譜の記録（部品(Primitive) H-10）と一緒に、中身でアドレスしたキーへ冪等書込の継手（C-05）で書きます。fn は同じプロセスの中で動き、安全のための隔離ではありません。",
        "problem": "集計表の数字がどの入力から出たかは、表に出ません。この部品は、入力の集合・集計器の版・出力のハッシュを系譜として残します。あわせて、検めていない入力で集計すること、集計器に保管物を書き換えさせること、出力で入力の場所を上書きすることを防ぎます。",
        "use_when": [
          "1〜1000 個の入力（key と sha256）を検めて読み、信頼できる集計器 fn で集計し、出力を out/<tool ref>/<入力の集合の digest>/<出力の sha256>.json に 1 回だけ書きたいとき（run）。",
          "fn に、キーの順の凍った配列 [{ key, sha256, data }] だけを渡し、元の bytes や読んだ値そのものは渡したくないとき。",
          "出力の隣に、入力の Ref（<key>@sha256:<hex>）・集計器の ref と version・出力のハッシュを結んだ系譜の記録を残したいとき。",
          "ほかの集計器の出力を、次の集計の入力にしたいとき。出力の場所は集計器ごとで、自分の出力の場所と重ならない入力は読めます。",
          "結果の分からなかった書込を、同じ要求で送り直したいとき（resolvePending）。同じ run をもう一度すれば、出力は DEDUP_HIT になります。"
        ],
        "do_not_use_when": [
          "信頼できない集計器を隔離して動かしたいとき。fn は同じプロセスで動き、process・require・グローバルの値・時計・乱数・ネットワークに触れられ、時間とメモリの上限もありません。この部品が守るのは「入力を書き換えさせない」ことだけです。",
          "集計の正しさや、fn が決定的であることを確かめたいとき。系譜は使った入力・集計器の版・出力のハッシュの記録で、集計の正しさの証明ではありません。",
          "fn に小数を返させたいとき。出力は RFC 8785（rfc8785-restricted/v1）なので、小数は受け取りません。",
          "入力・出力の大きさの上限をこの部品に任せたいとき。上限は読出しと書込の継手の設定で決めます。",
          "入力の JSON の重複したキーや、安全な整数を超える数を検めたいとき。JSON.parse のとおりに読み、fn に渡る値として検めません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違うとき INVALID_OPTIONS、run の入力の形が違うとき INVALID_INPUT、同じキーが 2 回以上あるとき DUPLICATE_INPUT、入力のキーが出力の場所 out/<tool ref> と同じかその下にあるとき INPUT_OUTPUT_OVERLAP で、何も読みません。",
          "C-01 の継手が NOT_FOUND を返したとき INPUT_NOT_FOUND、GET_REJECTED を返した・自分で計算し直した sha256 が違うとき INPUT_DIGEST_MISMATCH、結果が分からないとき READ_UNKNOWN、送らなかったとき READ_REFUSED、停止・形の違いのとき READ_FAILED で、fn を呼ばず何も書きません。",
          "入力の bytes が UTF-8 の JSON でないとき（BOM を含む）、INPUT_NOT_JSON で fn を呼びません。",
          "fn が例外を投げたとき（凍った入力を strict mode で書き換えようとした場合を含む）AGGREGATOR_FAILED、fn の値が RFC 8785 で正準化できない・同期の値でないとき OUTPUT_NOT_JSON（reason を添える）で、何も書きません。",
          "H-10 が例外を投げたとき LINEAGE_FAILED、ok: true を返さないとき LINEAGE_REFUSED、記録の形や count・出力の digest と大きさ・tool・recorded_at・schema が今回の実行と合わないとき LINEAGE_INVALID で、何も書きません。",
          "書込の結果が分からないとき WRITE_UNKNOWN で保留にし、保留の間の run を WRITE_PENDING で止めます。送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。",
          "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
        ],
        "what_it_does_not_do": [
          "読み取り専用の集計の実行の部品は継手ではありません。自分の Request/Receipt は無く、書込という外への効果は、束ねた C-05 の継手が自分の契約で起こします（読出しは NONE）。この部品自身は外へ触れません。",
          "束ねた部品の意味（読めた ≠ 中身の正しさ・系譜は集計の正しさの証明ではない・書けた ≠ 受け入れ）は、それぞれの部品の側に残ります。集計器そのものと集計の正しさは、この部品に入りません。",
          "書けたことは、集計の正しさでも承認でもありません（approval: false・authority: NOT_DERIVED・aggregation_correctness: NOT_VERIFIED、derives_authority: false）。",
          "fn を隔離しません（sandbox ではありません）。fn の時間とメモリにも上限を設けません。",
          "C-05 のキーと C-01 の object_ref が同じ保管物を指すか、出力を後で C-01 で読めるかは backend の設定の役目です。",
          "本物の object-store・storage-lifecycle の backend と H-10 の本物のコードには、この部品の試験では接続していません。単体試験とファズは代役です。状態（保留の書込）はプロセスの中だけで、再起動で消えます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の集計ツール・BI の雛形ではありません。"
        ],
        "integration": "src/sandbox-run.js の createSandboxRun(options) で作り、{ ok, errors, instance } を返します。options は output_target・request_prefix・ports・任意の timestamp です。run({ inputs, aggregator: { ref, version, fn } }) は、書けたとき output_key・lineage_key・written などを添え、結果に approval: false が付きます。port は read.get（C-01 の継手のクライアント）・lineage（H-10 の recordLineage）・write.submit（C-05 の継手のクライアント）です。副作用は束ねた継手が起こし、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "E-10",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Primitive",
        "title": "安全側の長さの写し — 安全側の長さの写しの部品（Primitive）",
        "summary": "安全側の長さの写しの部品（Primitive）は、P-05（制約・割当・時間枠）へ渡す前に、工程の見積りの幅 duration { min, max } を上限 { min: max, max: max } に写す単独の部品です。{ unknown: true } は変えず、ほかの欄はそのまま写します。元の min・max を projected に残し、変換の profile と写した入力の digest を返します。丸めず・詰めず、範囲の外と形の違う値は決まったコードと位置で止めます（fail-closed）。P-05 は呼びません。",
        "problem": "予定が「入る」と出た裏で、楽観の長さ（min）で置いたかは見えません。P-05 は工程を duration.min の長さで置くので、この写しを通すと max の長さで置かれます。たとえば不可分の 10〜20 分の工程は 20 分として扱われ、10 分の枠には入りません。どちらの長さで判定したかは、projection_profile と projected_tasks_digest で記録できます。",
        "use_when": [
          "工程の並び（0〜200 件）の duration を、見積りの上限 max で置く形に写してから P-05 に渡したいとき（projectDurations）。どの工程も { min, max } なら { max, max } になり、min = max ならそのままです。",
          "どの工程の長さを変えたかを、元の値つきで残したいとき。projected に、変えた工程だけを { id, from: { min, max }, to: { min, max } } で並びの順に載せます。",
          "どの入力を P-05 に渡したかを後から照らしたいとき。写した tasks の正準の JSON（キーを UTF-16 の符号単位の順、余白なし）の UTF-8 の sha256 を projected_tasks_digest として返します。",
          "長さの分からない工程（{ unknown: true }）を推し量らずにそのまま残し、その id を unknown に並べて受け取りたいとき。",
          "工程のほかの欄（requires・resource・window など）を、値として同じまま新しいデータに写したいとき。入力は書き換えません。",
          "例外を投げず、状態を持たない処理として、手元で呼びたいとき。時計・乱数・環境の locale を使わず、同じ入力には同じ答えを返します。"
        ],
        "do_not_use_when": [
          "楽観の長さ（min のまま）で判定したいとき。その判定はこの部品を通しません。P-05 が自分で max を使うのではなく、使う側がこの写しを明示して通します。",
          "配置の可否を知りたいとき。この部品は P-05 を呼ばず、配置の可否を言いません。P-05 の答え（feasible など）は P-05 の事実のままです。",
          "工程の形（requires・resource・window など）を検めたいとき。そのまま写すだけで、検めるのは P-05 です。",
          "見積りの max が正しいかを確かめたいとき。この部品は見ません。",
          "200 件を超える工程、100,000,000 分を超える長さ、32 段を超える入れ子の欄を扱いたいとき。決まったコードで止めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "tasks が素の配列でない（配列の派生クラスを含む）とき INVALID_TASKS、200 件を超えるとき TOO_MANY_TASKS で止めます。",
          "id が無い・getter・1〜128 の符号単位の文字列でない・単独のサロゲートがあるとき INVALID_ID、同じ id が 2 回あるとき DUPLICATE_ID（2 回目の位置）で止めます。",
          "duration が無い・getter、{ min, max }（ほかのキーなし）か { unknown: true } でない、min・max が整数でない・範囲の外、min が 1 より小さい、max が min より小さいとき、INVALID_DURATION で止めます。丸めず・詰めません。",
          "要素が素のオブジェクトでない（穴・null・継いだもの）、symbol・単独のサロゲートのキー、ほかの欄に getter・関数・undefined・整数でない数・安全でない整数・Date などの素でないオブジェクト・穴、入れ子 32 段を超えるとき、MALFORMED_TASK で止めます。",
          "値を調べること自体が例外になるとき（Proxy の罠など）、例外を出さずに INSPECTION_FAILED で止めます。getter は呼びません。",
          "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、写した tasks は返しません。",
          "結果を、別の書き方の独立オラクル（isWellFormed、比較関数での並べ替え、配列の線形の探索）と突き合わせるファズ（1,000,000 回）で、写した tasks・projected・unknown・digest と止めるコード・path の不一致、fail-open が 0 であることを確かめました。",
          "本物の P-05（solveSchedule）に不可分の 10〜20 分の工程と 15 分の枠を渡すと、そのままでは feasible、この部品で写した後は infeasible（INDIVISIBLE_TOO_LONG）になることを、手元で 1 例だけ確かめました。"
        ],
        "what_it_does_not_do": [
          "P-05 との結線は手元で 1 例だけ確かめました。部品の束は P-05 を含まないので、単体試験には入っていません。",
          "写すのは duration だけです。見積りの max が正しいかも、工程のほかの欄の形も検めません。",
          "配置はしません。写した結果は配置の可否ではなく、承認でもなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
          "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。P-05 の契約は変えていません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のスケジューラーの使い方の雛形ではありません。"
        ],
        "integration": "src/duration-projection.js の projectDurations(tasks) を呼びます（投げません。入力を書き換えません）。受け取るのは { id, duration, ... } の素のオブジェクトの素の配列（200 件まで）です。返すのは { ok: true, errors: [], projection_profile, tasks, projected, unknown, projected_tasks_digest } か { ok: false, errors: [{ code, path }] }。写した tasks を P-05 に渡すのは使う側です。副作用・状態・外の接続・資格情報は無く、依存は Node.js の組み込みの crypto だけで、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-01",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "レート制限・クォータ — レート制限・クォータの部品（Primitive）",
        "summary": "レート制限・クォータの部品（Primitive）は、キーごとのトークンバケット（token bucket）で、呼び出しの回数や量を抑える単独の部品です。バケットの量は整数で持ち、小数を使わずに補充します。足りなければ RATE_LIMITED と、待てばちょうど取れるようになる最短の retry_after_ms を返し、状態を変えません（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
        "problem": "「制限に達しました」を返す裏側で、1 つのプロセスの中の呼び出しの速さを抑えるときに、丸めで余計に通すこと、足りないのに量を減らしてしまうこと、知らないキーを無限に覚えてメモリーを使い切ることを防ぎます。",
        "use_when": [
          "1 つのプロセスの中で、キーごとに呼び出しの回数や量を抑えたいとき（take(key, n)）。",
          "止めたときに、待てばちょうど通る最短のミリ秒（retry_after_ms）を返したいとき。1 ms 前では通りません。",
          "バケットを減らさずに残りを見たいとき（peek は新しいキーを作りません）。キーを忘れて満杯に戻したいとき（reset）、満杯のバケットだけを忘れたいとき（prune）。",
          "時計を注入して、試験で結果を決定的にしたいとき。注入した時計の値の列が同じなら、同じ操作の列は同じ結果になります。",
          "例外を投げない呼び方が要るとき。ファクトリーとインスタンスのメソッドは例外を投げません。"
        ],
        "do_not_use_when": [
          "複数のプロセス・マシンで数を共有するレート制限が要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、ほかのプロセス・インスタンス・マシンとは共有しません。分散したレート制限ではありません。",
          "再起動をまたいで数を残したいとき。プロセスを再起動すると数は消え、すべてのキーが満杯に戻ります。",
          "プロバイダーのクォータ・上限を問い合わせたり変えたりしたいとき。それは外の境界で、別の継手の役目です。",
          "時間の窓で数える方式（固定窓・スライド窓）や、キーごとに違う上限が要るとき。",
          "スレッド（worker_threads）の間で 1 つのインスタンスを共有したいとき。1 つのイベントループの中で使う前提です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "トークンが足りないとき、RATE_LIMITED と remaining・retry_after_ms を返し、バケットを変えません。",
          "options がオブジェクトでない・読むと例外が起きるとき INVALID_OPTIONS、知らないオプションのとき UNKNOWN_OPTION、型・範囲が違うとき INVALID_OPTION（field に名前）で止めます。書き間違いを黙って無視しません。",
          "空から満杯までの時間が安全な整数のミリ秒を超えるとき、retry_after_ms を正確に返せないので REFILL_TOO_SLOW で止めます。",
          "key が 1〜256 文字の文字列でないとき INVALID_KEY、n が 1 以上の安全な整数でないとき INVALID_AMOUNT、n が capacity を超えるとき REQUEST_TOO_LARGE で止めます。",
          "新しいキーが max_keys を超えるとき、状態を作らずに TOO_MANY_KEYS で止めます。",
          "時計が受け取った一番新しい時刻より前に戻ったとき CLOCK_WENT_BACKWARDS、clock.now() が例外を投げたとき CLOCK_FAILED、安全な整数を返さないとき INVALID_CLOCK_VALUE で止めます。"
        ],
        "what_it_does_not_do": [
          "複数のプロセス・マシンの間で数を共有しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。分散したレート制限（共有の記憶が要るもの）はこの部品ではありません。",
          "名前の「クォータ」のうち、プロバイダーのクォータ・上限の問い合わせや変更はしません。それは外の境界で、別の継手の役目です。この部品はプロセスの中の数え方だけです。",
          "プロバイダーの API のクォータ・レート制限の数え方と一致するとは確かめていません。",
          "進みすぎた時計（未来の値）は見分けられません。既定の時計（process.hrtime.bigint）の単調さは Node.js に任せています。",
          "誰にどれだけ許すかという業務の意味は決めません。それは Kernel と使う側の役目です。",
          "通したことは、その呼び出しを行ってよいという許可でも、プロバイダーの側のクォータが残っていることの証明でもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のレート制限のライブラリやサービスの使い方の雛形ではありません。"
        ],
        "integration": "src/rate-limit.js の createRateLimiter(options) を呼びます（投げません）。options は { capacity, refill_tokens, refill_interval_ms, max_keys, clock } だけで、clock は任意。返すのは { ok, errors, instance }。instance の take(key, n) は { ok, errors, remaining, retry_after_ms }、peek・reset・prune は remaining・removed・keys を返します。止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリー、依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-02",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "サーキットブレーカ — サーキットブレーカの部品（Primitive）",
        "summary": "サーキットブレーカの部品（Primitive）は、直近の件数の窓で失敗を数え、CLOSED → OPEN → HALF_OPEN → CLOSED／OPEN と移る単独の部品です。呼ぶ前に allow() で聞き、結果を record({ outcome }) で渡します。失敗かどうかは呼ぶ側が決めます。対応する allow の無い結果は UNKNOWN_TRIAL で止めて数えません（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
        "problem": "1 つのプロセスの中で、失敗の続く相手への呼び出しを一時止めるときに、前の呼び出しの遅れた結果や前の回の試しで HALF_OPEN が勝手に閉じてしまうこと、止めたときに状態が変わってしまうことを防ぎます。",
        "use_when": [
          "1 つのプロセスの中で、失敗の続く相手への呼び出しを一時止めたいとき。呼ぶ前に allow() で聞き、結果を record で渡します。",
          "直近 window_size 件の中の FAILURE の件数（failure_threshold 件以上）で OPEN にしたいとき。",
          "OPEN の間に、次に試せるまでのちょうどのミリ秒（retry_after_ms）を返したいとき。",
          "HALF_OPEN の試しを trial_id で結びつけ、どの結果がどの試しのものかを取り違えたくないとき。",
          "時計を注入して、試験で結果を決定的にしたいとき。乱数を使いません（trial_id は通し番号）。",
          "例外を投げない呼び方が要るとき。ファクトリーとインスタンスのメソッドは例外を投げません。"
        ],
        "do_not_use_when": [
          "複数のプロセス・マシンで状態を共有するブレーカが要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、ほかのプロセス・インスタンス・マシンとは共有しません。",
          "再起動をまたいで OPEN を保ちたいとき。プロセスを再起動すると CLOSED の空の窓に戻り、相手がまだ落ちていても数え直します。",
          "時間の窓（直近 N 秒）・失敗の率・遅い呼び出しで数えたいとき。件数の窓だけです。",
          "HALF_OPEN で複数の試しの成功を待ってから閉じたいとき。最初に届いた試しの結果で決めます。",
          "試しの結果を必ず record できないとき。試しの期限は持たず、結果が来ないと HALF_OPEN_BUSY のまま次の試しを出しません。",
          "スレッド（worker_threads）の間で 1 つのインスタンスを共有したいとき、または Node.js v24.21.0 のほかの版で使うとき（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "OPEN の間、allow() は CIRCUIT_OPEN と retry_after_ms を返します。1 ms 前では試しを出しません。",
          "対応する allow の無い結果（HALF_OPEN・OPEN で trial_id が無い、知らない trial_id、前の回や無効になった試しの trial_id、CLOSED で trial_id がある）のとき、数えずに UNKNOWN_TRIAL で止め、状態を変えません。",
          "この回の試しを half_open_max_trials 件出し終えて結果がまだ来ていないとき、HALF_OPEN_BUSY で止めます。",
          "record の引数が { outcome, trial_id? } だけのオブジェクトでない・trial_id が文字列でないとき INVALID_RECORD、outcome が \"SUCCESS\"・\"FAILURE\" でない（大文字小文字も区別）とき INVALID_OUTCOME で止めます。",
          "options がオブジェクトでないとき INVALID_OPTIONS、知らないオプションのとき UNKNOWN_OPTION、型・範囲が違うとき（failure_threshold が window_size を超えるものを含む）INVALID_OPTION で止めます。",
          "時計が受け取った一番新しい時刻より前に戻ったとき CLOCK_WENT_BACKWARDS、clock.now() が例外を投げたとき CLOCK_FAILED、安全な整数を返さないとき INVALID_CLOCK_VALUE で止めます。"
        ],
        "what_it_does_not_do": [
          "複数のプロセス・マシンの間で状態を共有しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。共有するブレーカはこの部品ではありません。",
          "名前の「障害の連鎖を止める」のうち、どの結果を失敗とみなすか（例外・応答を見た分類）はしません。呼ぶ側が決めた SUCCESS／FAILURE だけを数えます。",
          "止めたときに何をするか（F-07 の代わりの候補・F-03 の再試行など）は決めず、呼び出しそのものもしません。使う側の役目です。",
          "state() は時計を読みません。open_ms たった後も、allow() を呼ぶまで OPEN を返します。",
          "進みすぎた時計（未来の値）は見分けられません。既定の時計（process.hrtime.bigint）の単調さは Node.js に任せています。ほかのサーキットブレーカのライブラリの移り方・既定値と同じだとは言いません。",
          "allow() が通したことは、その呼び出しを行ってよいという許可でも、相手が回復したことの証明でもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のサーキットブレーカのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/circuit-breaker.js の createCircuitBreaker(options) を呼びます（投げません）。options は failure_threshold・window_size・open_ms・half_open_max_trials・clock（任意）だけ。返す instance の allow() は state・trial_id?・retry_after_ms?、record({ outcome, trial_id? }) は state、state() は窓の件数・opened_at などを返します。どれも { ok, errors } を持ち、止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-03",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "リトライ（指数バックオフ） — リトライ（指数バックオフ）の部品（Primitive）",
        "summary": "リトライ（指数バックオフ）の部品（Primitive）は、1 つの操作の再試行を数え、次の試行の時刻を決める単独の部品です。遅延は上限つきの指数バックオフ（exponential backoff）を整数の ms で計算し、jitter \"full\" では注入された乱数で選びます。操作は実行せず、待ちもしません。冪等でない（idempotent: false）操作の再試行は NOT_IDEMPOTENT で止めます（fail-closed）。状態はインスタンスごと・このプロセスのメモリーの中だけです。",
        "problem": "「たまに直る」一時失敗を再試行するときに、冪等でない操作を再試行してしまうこと、idempotent の言い忘れを通してしまうこと、無限の再試行や遅延の桁あふれを起こすこと、知らないキーを黙って無視することを防ぎます。",
        "use_when": [
          "1 つの操作の再試行の回数（max_attempts。最初の試行を含む）と、次の試行の時刻（not_before_ms）を、1 つのプロセスの中で管理したいとき。",
          "上限つきの指数バックオフの遅延 min(max_ms, base_ms × multiplier^(attempt-1)) を、整数の ms で計算したいとき（computeDelay は状態を持たない純関数です）。",
          "jitter \"full\" で、[0, 遅延] の整数の遅延を注入した乱数で選びたいとき。",
          "冪等でない操作は、最初の試行だけを許し、再試行を止めたいとき（idempotent: false）。",
          "時計と乱数を注入して、試験で結果を再現したいとき。",
          "例外を投げない呼び方が要るとき。工場・computeDelay・インスタンスの操作は例外を投げません。"
        ],
        "do_not_use_when": [
          "複数のプロセス・インスタンスの再試行の合計を数えたいとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、プロセスやインスタンスをまたいで共有しません。",
          "再起動をまたいで試行の回数を残したいとき。状態はプロセスが終われば消えます。",
          "操作の実行や、not_before_ms までの待ち（タイマー）を部品に任せたいとき。どちらも呼ぶ側の役目です。",
          "Equal Jitter・Decorrelated Jitter や、整数でない multiplier（1.5 など）が要るとき。jitter は none と full だけ、multiplier は整数だけです。",
          "予測できない乱数の質が要るとき。注入した乱数は、戻り値が範囲の整数かだけを検めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "idempotent: false の操作で RETRYABLE_FAILURE を記録した後は HALTED になり、次の試行を NOT_IDEMPOTENT で止めます（回数が残っていても）。idempotent が無い・真偽値でないときは INVALID_IDEMPOTENT で止めます（既定値を置きません）。",
          "not_before_ms より前の試行のとき TOO_EARLY（not_before_ms と retry_after_ms を付けて）、試行の回数が max_attempts に達したとき RETRIES_EXHAUSTED で止めます。",
          "試行と記録が交互でないとき（試行中の nextAttempt は ATTEMPT_IN_FLIGHT、試行の無い record は NO_ATTEMPT_IN_FLIGHT）、終わった後の試行・記録は RUN_FINISHED で止めます。",
          "record の input が { outcome } だけでない、または outcome が SUCCESS・RETRYABLE_FAILURE・FINAL_FAILURE でないとき、INVALID_OUTCOME で止めます。",
          "options・policy が素のオブジェクトでないとき INVALID_OPTIONS、知らないキーのとき UNKNOWN_OPTION（key を付けて）、attempt・base_ms・max_ms・multiplier・jitter・max_attempts が範囲の外のとき、それぞれの INVALID_* で止めます。",
          "random が関数でないとき INVALID_RANDOM、[0, 遅延] の整数を返さない・例外を投げたとき RANDOM_OUT_OF_RANGE で止めます。",
          "clock が now() を持たないとき INVALID_CLOCK、clock.now() が 0 以上の安全な整数を返さない・例外を投げたとき CLOCK_INVALID、前に読んだ値より戻ったとき CLOCK_WENT_BACKWARDS で止めます。",
          "止めた操作（ok: false）は、試行の回数・段階・not_before_ms を変えません。"
        ],
        "what_it_does_not_do": [
          "状態はこのプロセスのメモリーの中だけで、プロセスやインスタンスの間で共有せず、再起動で消えます。複数のプロセスにまたがる再試行の数え方には使えません。",
          "名前の「一時失敗の再試行」のうち、再試行の実行はしません。操作の実行と not_before_ms までの待ち（タイマー）は呼ぶ側です。",
          "失敗が一時的か（RETRYABLE か FINAL か）、業務として再試行してよいかは決めません。idempotent と outcome は呼ぶ側の申告をそのまま使い、正しいかを確かめません。",
          "相手の Retry-After やレート制限の応答は読みません（継手の側）。全体の待ち時間の制限は F-06、呼ぶこと自体を止めるのは F-02 の役目です。",
          "時計の単調さは、戻ったときに止めるだけで、保つのは注入した側です。ファズの手本は実装と同じ書き手が同じ仕様から別に書いたもので、仕様の読み違いが両方にあれば見つかりません。",
          "nextAttempt が ok でも、操作を実行してよいという許可ではありません。retry_allowed: true は業務として再試行すべきかの判断ではありません（result_is_approval: false、derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のリトライのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/retry.js の createRetryRun(options) を呼びます（投げない工場）。options は max_attempts・base_ms・max_ms・multiplier・jitter・idempotent（必須）・clock・random だけ。返す instance の nextAttempt() は attempt・not_before_ms、record({ outcome }) は state・retry_allowed、snapshot() は状態を返します。computeDelay(policy, random) は delay_ms を返す純関数です。どれも { ok, errors } を持ち、止めたときは code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-04",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "冪等キー処理 — 冪等キー処理の部品（Primitive）",
        "summary": "冪等キー処理の部品（Primitive）は、新しい要求（段・op_id・中身の digest）を、渡された台帳（前に受けた要求と、その結果の digest）と比べて、はじめて（NEW）・同じものの送り直しで結果あり（DUPLICATE_SAME）・同じものの送り直しで結果がまだ無い（DUPLICATE_PENDING）・同じ ID の別の中身（CONFLICT）のどれかに分ける単独の部品です。段は REQUEST・ATTEMPT・DELIVERY で、段ごとに別に数えます。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
        "problem": "再送で同じ処理が二重に動くことを防ぐための見分けです。同じ ID で中身の違う要求を、同じものの送り直しとして通すこと、結果の分からない送り直しを新しい実行として扱うこと、形の違う台帳の行を黙って読み飛ばすことを防ぎます。",
        "use_when": [
          "新しい要求が、渡された台帳にある要求の送り直しかどうかを、冪等の鍵（段・op_id）と中身の digest（payload_digest）の完全一致で見分けたいとき（classify）。",
          "同じものの送り直しに、前の結果の digest を返したいとき。DUPLICATE_SAME の result_digest は、台帳のその行の result_digest そのものです。",
          "結果がまだ無い行（result_digest が null）への送り直しを、もう一度動かさない印 DUPLICATE_PENDING として受け取りたいとき。もう一度動かす判断にはせず、照合を待つための印です。",
          "同じ ID で中身が違う要求を CONFLICT として見分けたいとき。同じ ID の中身の違いを見ずに、ID だけで重複を決める見分けとは違います。",
          "要求の受付（REQUEST）・試行（ATTEMPT）・届け（DELIVERY）を段ごとに別に数えたいとき。段が違えば、同じ op_id でも比べません。counts に段ごとの別々の op_id の数を返します。",
          "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。同じ入力には同じ答えを返します。"
        ],
        "do_not_use_when": [
          "台帳の保存・読み出しや、ほかの呼び出し・保存済みの台帳との重複を見たいとき。比べるのは引数の中だけで、台帳を保存・読み出すのは Host など、この部品の外の側です。",
          "同時に来た 2 つの要求の競り合いを決めたいとき。どちらを先にするかは、台帳を書く側（Host）が決めます。",
          "要求を断る・記録する・知らせる、または送り直しを実行したいとき。CONFLICT は断る要求の印を返すだけで、断る・記録する・知らせるのは使う側の仕事です。送り直しの実行・時計・保存もしません。",
          "見た目が同じ op_id（é と e + 結合文字、A と a、前後の空白の違い）を同じものとして扱いたいとき。op_id は UTF-16 の符号単位の完全一致だけで比べます。",
          "digest を大文字・sha256: の接頭辞つきで渡したいとき。digest は変換せず、小文字 16 進 64 桁のほかは INVALID_DIGEST で止めます。中身から digest を作る規則（正準化 I-08、ハッシュ B-07 など）は使う側が決め、台帳と要求の両方を同じ規則で作ります。",
          "100,000 行を超える台帳を 1 回で扱いたいとき。使う側で分けます。分けると、分けた先の行との重複は見分けません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "request が素のオブジェクトでない（継いだもの・クラスのもの・null の原型を含む）、stage・op_id・payload_digest が自分のデータのプロパティでない（無い・getter）とき、MALFORMED_REQUEST で止めます。",
          "ledger が素の配列でないとき INVALID_LEDGER、100,000 行を超えるとき TOO_MANY_ENTRIES、行が素のオブジェクトでない（穴・getter の行を含む）か 4 つの欄が自分のデータのプロパティでないとき MALFORMED_ENTRY で止めます。",
          "stage が REQUEST・ATTEMPT・DELIVERY のどれでもない（小文字・箱の文字列を含む）とき INVALID_STAGE、op_id が文字列でない・空のとき INVALID_OP_ID、256 の UTF-16 の符号単位を超えるとき OP_ID_TOO_LONG で止めます。",
          "payload_digest が小文字 16 進 64 桁の文字列でない（大文字・接頭辞・桁違い・16 進でない・箱の文字列・配列・null）、result_digest がその形の文字列でも null でもないとき、INVALID_DIGEST で止めます。",
          "台帳に同じ（段・op_id）の行が 2 回あるとき、中身が同じでも DUPLICATE_LEDGER_ENTRY（2 回目の位置）で止めます。値を調べること自体が例外になるとき（Proxy の罠など）は INSPECTION_FAILED です。",
          "__proto__・constructor などの名前も、ただの op_id として比べます（Map で比べ、オブジェクトの名前に使いません）。getter は呼びません。",
          "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、decision などは返しません。",
          "結果を、Map も Set も使わずに台帳を前から順に探す書き方の独立オラクルと突き合わせるファズ（1,000,000 回）で、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "NEW は「渡された台帳に無い」だけで、実行してよいという意味ではありません。渡されていない台帳（ほかの場所・保存前のもの）との重複は分かりません。",
          "中身の digest が本当に中身から作られたかは見ません（渡された digest を比べるだけ）。DUPLICATE_SAME の結果の digest が正しいかも見ません。",
          "上限ちょうどの 100,000 行を受け取ることは、単体試験だけで確かめました（ファズは上限を超えるものだけ）。",
          "外の境界・状態・時計・乱数を持たず、保存もしません。副作用は NONE で、資格情報も使いません。",
          "見分けの結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の冪等キーのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/idempotency-ledger.js の classify(request, ledger) を呼びます（投げません。入力を書き換えません）。受け取るのは { stage, op_id, payload_digest } の素のオブジェクトと、{ stage, op_id, payload_digest, result_digest } の行の素の配列（100,000 行まで）です。返すのは { ok: true, errors: [], decision, matched_index, result_digest, counts } か { ok: false, errors: [{ code, path }] }。副作用・状態・外の接続・資格情報は無く、依存は Node.js の組み込みだけで、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-05",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "同時実行制限（bulkhead） — 同時実行制限（bulkhead）の部品（Primitive）",
        "summary": "同時実行制限（bulkhead）の部品（Primitive）は、区画（compartment）ごとに、同時に動かす数（max_concurrent）と待ち行列の長さ（max_queue）を制限する単独の部品です。空きがあれば token、満員で待ち行列に空きがあれば ticket を渡し、どちらも満杯なら BULKHEAD_FULL で止めます（fail-closed）。制限はインスタンスごと・このプロセスのメモリーの中だけで、分散の制限ではありません。",
        "problem": "1 つのプロセスの中で、一部の仕事の暴走が同じプロセスのほかの仕事を巻き添えにしないように区画を分けるときに、上限を超えて枠を渡すこと、二重の release や偽の token で枠を余計に空けてしまうことを防ぎます。",
        "use_when": [
          "1 つのプロセスの中で、名前で区切った区画ごとに同時に動かす数と待ち行列の長さを制限したいとき。区画どうしは独立で、ある区画が満杯でもほかの区画に影響しません。",
          "満員のときに、待ち行列に入れて FIFO で繰り上げたいとき。release の結果の promoted に繰り上がった token が入り、待っている側は poll で知ります。",
          "待たずにすぐ断りたい区画があるとき（max_queue 0）。",
          "待ち行列から外したいとき（cancel）。cancel した ticket は繰り上がりません。",
          "例外を投げない呼び方が要るとき。工場とインスタンスの操作は例外を投げません。"
        ],
        "do_not_use_when": [
          "複数のプロセス・マシンをまたぐ同時実行の制限（分散のセマフォ・共有の数え上げ）が要るとき。状態はインスタンスごと・このプロセスのメモリーの中だけで、プロセスやインスタンスをまたいで共有しません。",
          "再起動をまたいで枠の数を残したいとき。状態は再起動で消えます。",
          "待ちの期限（タイムアウト）が要るとき。待ちすぎを止めるのは F-06 か呼ぶ側です。",
          "待っている側を通知・Promise で起こしたいとき。この部品は待っている側を起こしません。",
          "release を忘れた token の枠を自動で回収したいとき。期限による回収・漏れの検出はありません。",
          "優先度・重み・公平さの調整が要るとき。区画の中は FIFO だけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "区画の枠も待ち行列も満杯のとき、BULKHEAD_FULL（compartment に名前）で止めます。上限を超えて枠を渡しません。",
          "このインスタンスが渡した token そのものでないとき（同じ形の偽物・別のインスタンスの物・ticket との取り違え）UNKNOWN_TOKEN、もう release した token のとき TOKEN_ALREADY_RELEASED で止め、枠を空けません。",
          "このインスタンスが渡した ticket でないとき UNKNOWN_TICKET、cancel 済みのとき TICKET_CANCELLED、繰り上がった後の cancel のとき TICKET_ALREADY_GRANTED で止めます。",
          "acquire の区画名が作った時の区画に無いとき（constructor・toString などのプロトタイプの名前を含む）、UNKNOWN_COMPARTMENT で止めます。",
          "options が素のオブジェクトでないとき INVALID_OPTIONS、知らないキーのとき UNKNOWN_OPTION、区画が 0 個か 257 個以上のとき INVALID_COMPARTMENTS、区画名・区画の値・max_concurrent・max_queue の形や範囲が違うとき、それぞれの INVALID_* で止めます。",
          "clock が now() を持たないとき INVALID_CLOCK、clock.now() が 0 以上の安全な整数を返さない・例外を投げたとき CLOCK_INVALID、前に読んだ値より戻ったとき CLOCK_WENT_BACKWARDS で止めます。",
          "止めた操作（ok: false）は、枠・待ち行列・token・ticket を変えません。"
        ],
        "what_it_does_not_do": [
          "複数のプロセス・マシンの合計は制限しません。状態はこのプロセスのメモリーの中だけで、再起動で消えます。分散の制限は外の境界を持つので、この部品ではなく継手の側です。",
          "名前の「一部の暴走を隔離」のうち、暴走の検出や停止はしません。区画ごとに同時に動かす数と待ち行列の長さを制限するだけです。",
          "待ちの期限・待っている側の起こし方・release を忘れた枠の回収は持ちません。",
          "token は同じプロセスの中の取っ手で、所有の証明・権限・実行の許可ではありません。受け取った側がほかへ渡すことは止めません（result_is_approval: false、derives_authority: false）。",
          "ファズの手本は実装と同じ書き手が同じ仕様から別に書いたもので、仕様の読み違いが両方にあれば見つかりません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のセマフォ・プールのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/bulkhead.js の createBulkhead({ compartments, clock? }) を呼びます（投げない工場）。返す instance の acquire(name) は token（queued: false）か ticket（queued: true）、release(token) は compartment・promoted、poll(ticket) は granted と token、cancel(ticket) は compartment、snapshot() は区画ごとの active・queued を返します。どれも { ok, errors } を持ち、止めたときは errors の code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-06",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "タイムアウト・期限伝播 — タイムアウト・期限伝播の部品（Primitive）",
        "summary": "タイムアウト・期限伝播の部品（Primitive）は、注入された単調な時計（clock.now()、ミリ秒）の上に絶対の期限を作り、親から子へ期限と取消しを伝える単独の部品です。子の期限は親より延びません。取消しはこのプロセスの中の印で、外の処理が止まったことは意味しません。受け取れない値・戻った時計は決まったコードで止めます（fail-closed）。状態はこのプロセスのメモリーの中だけです。",
        "problem": "1 つのプロセスの中で処理を重ねて呼ぶときに、子の期限が親より延びてしまうこと、時計が戻って期限切れが取り消されてしまうこと、取消しが子孫に伝わらないことを防ぎ、待ちすぎ（ハング）を読み取れるようにします。",
        "use_when": [
          "1 つのプロセスの中で、処理の期限を作り、呼び出しの先へ残り時間（remaining_ms）を渡したいとき。外の API を呼ぶ継手に残り時間を渡す側の共通の部品になる候補です。",
          "子の期限を min(親の期限, 今 + 自分の timeout) にして、伝播で期限が広がらないようにしたいとき（child）。",
          "自分と子孫すべてに取消しの印をつけたいとき（cancel）。cancel は時計を読まないので、時計が壊れていても取消しはできます。",
          "期限の状態（ACTIVE・EXPIRED・CANCELLED）と、取消しの理由・由来（SELF・ANCESTOR）を読みたいとき（status・expired）。",
          "時計を注入して、試験で結果を決定的にしたいとき。壁時計（Date）・タイマー・乱数は使いません。",
          "例外を投げない呼び方が要るとき。工場も instance のメソッドも例外を投げません。"
        ],
        "do_not_use_when": [
          "プロセスをまたぐ・分散した期限の伝播（ヘッダーで送るなど）が要るとき。状態はこのプロセスのメモリーの中だけで、プロセス間・分散の共有も永続化もしません。",
          "再起動をまたいで期限を残したいとき。プロセスを終えると状態は消えます。",
          "外の処理（相手先の API など）を取り消したいとき。取消しはこのプロセスの中の印だけで、外の取消しは使う側か継手がそれぞれの契約で行います。",
          "期限の時刻に知らせ（タイマー）が要るとき。使う側が読んだときに分かるだけです。",
          "1 つの木を長く持ち、期限を 10,000 個を超えて作るとき。期限を手放す操作は無く、終わった期限もメモリーに残って数に入ります。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "時計の読みが、その木で読んだ一番大きな値より小さいとき、記録を変えずに CLOCK_WENT_BACKWARDS で止めます。一度 EXPIRED になった期限は ACTIVE に戻りません。",
          "期限切れか取消し済みの期限に子を作ろうとしたとき、DEADLINE_ALREADY_OVER で止めます。",
          "timeout_ms が 1〜2,147,483,647 の安全な整数でないとき（0・負・小数・NaN・文字列・上限超え）、INVALID_TIMEOUT で止めます。",
          "clock が now() を持たないとき INVALID_CLOCK、clock.now() が投げた・0〜MAX_CLOCK_MS の有限の数を返さないとき INVALID_CLOCK_READING、parent の木と別の clock を渡したとき CLOCK_MISMATCH で止めます。",
          "parent がこの部品が作った期限の instance でないとき（写しを含む）、INVALID_PARENT で止めます。",
          "選択肢がオブジェクトでない・許されないキーがある・読むと投げるとき INVALID_OPTIONS、reason_code が英大文字で始まる英大文字・数字・_ の 1〜64 文字でないとき INVALID_REASON で止めます。",
          "木の期限の数が 10,000 個（MAX_DEADLINES_PER_TREE）に達したとき、TOO_MANY_DEADLINES で止めます。"
        ],
        "what_it_does_not_do": [
          "状態はこのプロセスのメモリーの中だけで、プロセス間・分散で共有せず、プロセスを終えると消えます。名前の「期限伝播」は、このプロセスの中の親から子への伝播だけで、プロセスをまたぐ伝播（ヘッダーで送るなど）はしません。",
          "名前の「タイムアウト」のうち、処理を止めることはしません。取消しはこのプロセスの中の印（local_only: true）で、外の処理の取消しや、その成否は扱いません。外の取消しは各継手とその契約の側です。",
          "期限の時刻に知らせることはしません（タイマーを持ちません）。",
          "注入した時計が本当に単調かは確かめられません（戻った読みを止めるだけ）。既定の時計の単調さは Node.js の process.hrtime.bigint に依ります。どの継手・ライブラリの期限の形とも互換を確かめていません。",
          "残り時間・期限切れ・取消しは、この部品の時計と印の上の事実だけで、外の処理が止まったこと・終わったこと・やり直してよいことではありません（result_is_approval: false、derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のライブラリ（AbortController など）の使い方の雛形ではありません。"
        ],
        "integration": "src/deadline.js の createDeadline({ timeout_ms, clock?, parent? }) を呼びます（投げない工場）。返す instance の remaining() は remaining_ms・state、expired() は expired・cancelled・over、child({ timeout_ms }) は子の instance、cancel({ reason_code }) は newly_cancelled・local_only: true、status() は state・deadline_at・cancelled を返します。どれも { ok, errors } を持ち、止めたときは code（error_taxonomy）です。副作用（NONE）・外の接続・資格情報・port は無く、状態はプロセスのメモリーです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "F-07",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "フォールバック — フォールバックの部品（Primitive）",
        "summary": "フォールバックの部品（Primitive）は、外（方針・Owner・使う側）で決めた「並びつきの許可リスト」の通りに、失敗のあと次の候補へ進んでよいかを 1 歩ずつ決める単独の部品です。候補を足さず、並べ替えず、飛ばさず、許しを広げません。候補の呼び出しはせず、id を返すだけです。形の違う許可リスト・報告は決まったコードで止めます（fail-closed）。状態は実行 1 回分・このプロセスのメモリーの中だけです。",
        "problem": "「一部使えるが動く」ように次の候補へ切り替えるときに、決めていない候補へ移ること、並びを飛ばすこと、許されない失敗のあとで切り替えること、断られた後に別のコードで試し直すこと、作った後に許可リストを書き換えられることを防ぎます。",
        "use_when": [
          "AI の provider・model、保存先などの切り替えを、外で決めた並びの通りにだけ行わせたいとき。",
          "失敗のコードごとに、次の候補へ進んでよいかを決めたいとき。next は、失敗したのが今の候補で、そのコードがすぐ次の候補の allowed_after にあるときだけ、次の候補の id を返します。",
          "切り替えの歩数に上限（max_steps、0〜31）を置きたいとき。",
          "時計・乱数・環境を使わない、決定的な判定が要るとき。",
          "例外を投げない呼び方が要るとき。工場も instance のメソッドも例外を投げません。"
        ],
        "do_not_use_when": [
          "複数のプロセス・マシンで実行の状態を共有したいとき。状態は実行 1 回分・このプロセスのメモリーの中の instance だけで、プロセス間の共有も永続化もしません。",
          "再起動をまたいで今の候補・歩数を残したいとき。プロセスを終えると状態は消えます。",
          "どの候補を並べるか・どの失敗で切り替えてよいかを部品に決めさせたいとき。それは外（方針・Owner・使う側）で決めます。",
          "候補の呼び出し・待ち時間・再試行・遮断まで任せたいとき。期限は F-06、再試行は F-03、遮断は F-02 です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "error_code が、すぐ次の候補の allowed_after に無いとき、FALLBACK_NOT_ALLOWED で止め、その回の実行を終えます。断られた後に別のコードで試し直せません。",
          "failed_id が今の候補でないとき（前の候補・後の候補・知らない id）、状態を変えずに OUT_OF_ORDER で止めます。",
          "今の候補が最後のとき NO_MORE_CANDIDATES、歩数が max_steps に達したとき MAX_STEPS_REACHED で止め、その回の実行を終えます。終わった後の next は RUN_ENDED です。",
          "first を 2 回呼んだとき ALREADY_STARTED、first の前に next を呼んだとき NOT_STARTED、next の報告が failed_id と error_code だけのオブジェクトでないとき INVALID_REQUEST で止めます。",
          "candidates が配列でない・空・32 個を超えるとき INVALID_CANDIDATES、id の形が違うとき INVALID_CANDIDATE_ID、同じ id が 2 つあるとき DUPLICATE_CANDIDATE_ID で止めます。",
          "allowed_after の形が違うとき（形の違うコード・重なるコード・64 個超え・最初の候補で空でない・2 番目からの候補で空）INVALID_ALLOWED_AFTER、max_steps が 0〜31 の安全な整数でないとき INVALID_MAX_STEPS、選択肢の形が違うとき INVALID_OPTIONS で止めます。",
          "作った後に呼び出し側が許可リストの配列を書き換えても、結果は変わりません（写しを持ちます）。"
        ],
        "what_it_does_not_do": [
          "状態は実行 1 回分・このプロセスのメモリーの中だけで、プロセスやマシンの間で共有せず、プロセスを終えると消えます。プロセスをまたぐ・分散した実行の状態は扱いません。",
          "名前の「縮退運転」のうち、縮退した動作そのものはしません。provider や model を選ばず・切り替えず・呼び出さず、外で決めた並びの次の id を返すだけです。候補を呼ぶのは使う側と各継手です。",
          "許可リストの中身が正しいか（その候補に切り替えてよいか・費用や扱うデータの範囲が同じか）は判断しません。外で決めたものを守るだけです。",
          "失敗のコードが本当かは確かめられません。コードの意味も解釈せず、文字列の完全一致だけを見ます。",
          "次の候補の id は「外で決めた許可リストの上で次へ進んでよい」ことだけで、その候補を呼んでよい権限・費用の承認・結果の正しさではありません（result_is_approval: false、derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "どの継手・ライブラリのフォールバックの形とも、互換を確かめていません。"
        ],
        "integration": "src/fallback.js の createFallback({ candidates, max_steps }) を呼びます（投げない工場）。candidates は { id, allowed_after } の並び（1〜32 個）です。返す instance の first() は最初の候補 { id }、next({ failed_id, error_code }) は次の候補 { id }・index・step・allowed_by、status() は state（READY・ACTIVE・ENDED）・current・steps・end_code を返します。止めたときは errors の code（error_taxonomy）です。副作用（NONE）・時計・外の接続・資格情報・port は無く、候補を呼び出すのは使う側です。依存は無い純 JS です。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-01",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "決済オーソリ — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、決済オーソリの要求（PaymentAuthRequest）を、pin した支払い方法と、お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（PaymentAuthReceipt）を契約どおりに照合する継手（つぎて）です。どれかが外れれば、送らず、受理しません（fail-closed）。",
        "problem": "決済オーソリを外の決済基盤へ運ぶとき、許していないのにお金が動く操作を送ってしまうこと、pin していない支払い方法を使ってしまうこと、そしてお金が動いたか分からない receipt や業務の承認を名乗る receipt を信じてしまうことを防ぎます。決済の成功を業務の承認と取り違えないための検めです。",
        "use_when": [
          "決済オーソリ（AUTHORIZE）の要求を外の決済基盤へ運び、戻った receipt が op に合う状態（AUTHORIZED）かを照合してから結果として扱いたいとき。",
          "お金が動く操作を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
          "使ってよい支払い方法を、pin した一覧（method_refs。カード番号ではない参照）に限りたいとき。",
          "同じオーソリを二度動かしたくないとき。冪等キーを必須にし、同じ request_id は二度送らず、障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "決済できたことを、業務の承認として扱いたいとき。決済の成功は業務の承認ではありません（business_approval は運びから導きません）。",
          "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
          "オーソリ済みの記録を再起動の後も持ち続けたいとき。記録はこの継手の中だけで、再起動で消えます（永続化は接続するときに別の関門で決めます）。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
          "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "冪等キーを検めます。冪等キーの無いオーソリ、同じキーで別の中身の要求は送りません。",
          "オーソリの記録を検めます。この継手が AUTHORIZED と照合していない支払いの確定、取消の後の確定、二度目の確定は送りません。",
          "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
          "業務の承認を名乗る receipt は受理しません。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "決済の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に PaymentAuthRequest を渡すと、DONE（result に op・auth_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: オーソリの操作は EXTERNAL_IRREVERSIBLE、辞退・保留・失敗などは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-02",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "売上確定・取消 — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、売上の確定（CAPTURE）と取消（VOID）を、オーソリの要求（PaymentAuthRequest）として運ぶ継手（つぎて）です。確定・取消は、この継手が AUTHORIZED と照合し、まだ確定・取消していない支払いだけを送り、戻った receipt（PaymentAuthReceipt）を契約どおりに照合します。どれかが外れれば、送らず、受理しません（fail-closed）。",
        "problem": "オーソリした支払いを確定・取消するとき、オーソリしていない支払いを確定してしまうこと、取消した支払いを確定してしまうこと、同じ支払いを二度確定してしまうこと、そしてお金が動いたか分からない receipt や業務の承認を名乗る receipt を信じてしまうことを防ぎます。確定できたことを業務の承認と取り違えないための検めです。",
        "use_when": [
          "この継手でオーソリ（AUTHORIZED と照合済み）した支払いを、確定（CAPTURE→CAPTURED）または取消（VOID→VOIDED）として外の決済基盤へ運び、戻った receipt が op に合う状態かを照合してから結果として扱いたいとき。",
          "確定を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
          "確定・取消の後に、同じ支払いを二度確定しないようにしたいとき。",
          "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
        ],
        "do_not_use_when": [
          "ほかの経路でオーソリした支払いを確定・取消したいとき。確定・取消は、この継手が AUTHORIZED と照合した支払いだけです。",
          "オーソリ済みの記録を再起動の後も持ち続けたいとき。記録はこの継手の中だけで、再起動で消えます（永続化は接続するときに別の関門で決めます）。",
          "確定できたことを、業務の承認として扱いたいとき。決済の成功は業務の承認ではありません（business_approval は運びから導きません）。",
          "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "オーソリの記録を検めます。この継手が AUTHORIZED と照合していない支払いの確定・取消、取消の後の確定、二度目の確定は送りません（REFUSED）。",
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、確定を含むお金が動く操作は送りません（REFUSED）。",
          "参照を検めます。空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "receipt を照合します。op と合わない状態（CAPTURE なら CAPTURED、VOID なら VOIDED でないもの）、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
          "業務の承認を名乗る receipt は受理しません。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "確定の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "オーソリ済みの支払いの記録は、この継手の中だけに持ちます（再起動で消えます）。永続化はしません（接続するときに別の関門で決めます）。",
          "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に、op が CAPTURE か VOID の PaymentAuthRequest を渡すと、DONE（result に op・auth_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 確定・取消は EXTERNAL_IRREVERSIBLE、辞退・保留・失敗などは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-03",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "冪等課金 — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、冪等な課金の要求（IdempotentChargeRequest）を、冪等キー・pin した支払い方法・お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（IdempotentChargeReceipt）を契約どおりに照合する継手（つぎて）です。同じ冪等キーは重複として扱い、新しい効果を認めません（fail-closed）。",
        "problem": "課金を外の決済基盤へ運ぶとき、再送や重複で二度課金してしまうこと、同じキーで別の中身を送ってしまうこと、重複なのに効果を名乗る receipt を信じてしまうことを防ぎます。課金の成功を業務の承認と取り違えないための検めでもあります。",
        "use_when": [
          "二重課金を防ぐために、課金の要求に冪等キーを必須にし、同じキーの要求を重複（DEDUP_HIT、新しい効果なし）として扱いたいとき。",
          "課金を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
          "戻った receipt が要求に合う課金の状態かを照合してから、結果（charge_state・charged）として扱いたいとき。",
          "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
        ],
        "do_not_use_when": [
          "課金できたことを、業務の承認として扱いたいとき。課金の成功は業務の承認ではありません（business_approval は運びから導きません）。",
          "課金と入金の突き合わせをしたいとき。突き合わせは外の判断と人・組織の役目で、この継手はしません（対になる核は reconcile-facts です）。",
          "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
          "冪等キーを検めます。冪等キーの無い課金、同じキーで別の中身の要求は送りません。",
          "重複なのに効果を名乗る receipt は受理しません。重複（DEDUP_HIT）の効果は NONE です。",
          "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
          "業務の承認を名乗る receipt は受理しません。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "課金の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
          "額・通貨・税・手数料の正しさ、明細の突き合わせは確かめません（外の判断と人・組織の役目です）。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に IdempotentChargeRequest を渡すと、DONE（result に charge_state・charged・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 課金は EXTERNAL_IRREVERSIBLE、重複・辞退・失敗などは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-04",
        "category": "G/決済・資金",
        "kind": "Component",
        "title": "請求書生成 — 請求書生成の部品（Component）",
        "summary": "請求書生成の部品（Component）は、1 枚の請求書を、税の事実の計算（部品(Kernel) G-07）→ 文書（継手 J-01 の PDF_RENDER）→ 保存（継手 C-05、請求書番号を冪等キー）→ 記録（継手 H-01 の監査台帳）の段で発行する部品です。G-07 の事実に足りない入力や記載事項の欠けがあれば、文書を作らずに事実だけを返します。段の結果が分からないときはその段で止め、同じ番号・同じ中身の issue でその段から続けます（fail-closed）。請求書番号は呼び出し側が決めて渡します。",
        "problem": "会計の裏で、記載事項の欠けや足りない入力のある請求書が文書・保存・台帳に進んでしまうこと、同じ番号で別の中身の請求書を発行してしまうこと、保存を二重にしてしまうこと、請求書の中身を台帳へ渡してしまうことを防ぎます。",
        "use_when": [
          "1 枚の請求書を、計算 → 文書 → 保存 → 記録 の順で、前の段が済んでから次の段へ進めて発行したいとき（issue）。",
          "税の事実を G-07 の computeInvoiceTax で出させ、need_input や記載事項の欠けがあれば、文書を作らずにそのまま need_input・missing に添えて受け取りたいとき。invoice・tax_table・taxpayer_profile は G-07 の入力そのままです。",
          "PDF を、請求書番号を冪等キー（invoice/<番号>）にして、C-05 の継手で 1 回だけ保存したいとき。結果が APPLIED か DEDUP_HIT のときだけ保存済みです。",
          "発行の事実を、H-01 の継手で追記専用（append-only）の台帳に AuditEventEnvelope として記録したいとき。台帳へ渡すのは digest だけで、請求書の中身は渡しません。",
          "結果の分からなかった段を、同じ番号・同じ中身の issue で、同じ要求・同じ封筒のまま送り直したいとき。",
          "請求書番号ごとの段と digest を確かめたいとき（status）。"
        ],
        "do_not_use_when": [
          "発行できたこと（ISSUED）を、請求の承認・相手への送付・債権の成立・記載事項の法的な十分さとして使いたいとき。",
          "発行できたことを、税額が税務上正しいことを示すものとして使いたいとき。発行できたことは税務上正しいことを示すものではありません。税の事実は G-07 のままで、事実の正本は税率の表と事業者の選択です。",
          "請求書番号の採番・一意性・連番を任せたいとき。呼び出し側の役目です。",
          "相手への送付・受領、入金・債権の管理まで任せたいとき。外部の電子請求の送付・受領の継手は、この部品に入りません。",
          "PDF のレイアウトや、記載事項が PDF に実際に載っていることを確かめたいとき。G-07 の記載事項の検めは、入力の申告に対するものです。",
          "再起動をまたいで途中の段を続けたいとき。状態はこのプロセスの中だけで、再起動で途中の段と保った要求は消えます（保存は冪等キーで二重になりませんが、台帳の封筒は作り直されます）。",
          "本物の文書生成・保存・台帳の backend で確かめた部品が要るとき。本物の backend には一度も接続していません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき（欄の過不足・請求書番号の形など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "G-07 が ok: true を返さないとき TAX_INPUT_INVALID（tax_errors を添える）、G-07 の関数が投げたとき PORT_FAILED、事実の形が違う・inference か advice が null でないとき TAX_FACTS_INVALID で、文書を作りません。",
          "G-07 の事実に need_input があるとき NEEDS_INPUT（推測で埋めない）、記載事項の欠けがあるとき REQUIRED_ITEMS_MISSING（直し方は言わない）で、書類を作る前に止め、保存も記録もしません。",
          "同じ請求書番号で別の中身（請求書・税率の表・事業者の選択のどれか）のとき INVOICE_NUMBER_REUSED で止めます。発行済みの番号へのもう一度の issue は already_issued: true を返します。",
          "文書の継手の結果が分からないとき RENDER_UNKNOWN、送らなかったとき RENDER_REFUSED、変換できなかった・部分だけ・停止・空の PDF のとき RENDER_FAILED で、保存へ進みません。",
          "保存の継手の結果が分からないとき STORE_UNKNOWN（同じ要求を保つ）、送らなかったとき STORE_REFUSED（同じ冪等キーで別の中身を含む）、停止した・結果が APPLIED・DEDUP_HIT でないとき STORE_FAILED で、記録へ進みません。",
          "台帳の継手の結果が分からないとき（state() が投げた場合を含む）LEDGER_UNKNOWN（同じ封筒を保つ）、台帳が断った・先頭が進んで封筒がつながらない・先頭の形が不正なとき LEDGER_REFUSED で、発行済みにしません。",
          "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。"
        ],
        "what_it_does_not_do": [
          "請求書生成の部品は継手ではありません。自分の Request/Receipt は無く、文書の変換・保存・台帳への追記という外への効果は、束ねた J-01・C-05・H-01 の継手がそれぞれの契約で起こします。この部品自身は外へ触れません。",
          "束ねた部品の意味（事実だけ・変換できた ≠ 中身の確認・保存できた ≠ 受け入れ・記録できた ≠ 承認）は、それぞれの部品の側に残ります。",
          "発行できたことは、請求の承認・債権の成立・記載事項の法的な十分さではありません（approval: false・claim: NOT_DERIVED・legal_sufficiency: NOT_VERIFIED、derives_authority: false）。",
          "発行できたことは、税務上正しいことを示すものではありません。税務の判断はせず、事実だけを返し、直し方を返しません（税務相談は税理士の独占業務です）。",
          "請求書番号の採番・一意性・連番、相手への送付・受領、請求の承認、入金・債権の管理はしません。",
          "本物の文書生成・保存・台帳の backend には一度も接続していません。単体試験とファズは代役で、本物の 4 部品のコードとの結線は、各継手の試験用の代役を相手に確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の請求書ソフトの使い方の雛形ではありません。"
        ],
        "integration": "src/invoice.js の createInvoiceIssuance(options) で作ります。受け取るのは参照・store_target・ledger・ports・任意の timestamp で、{ ok, errors, instance } を返します。issue と status の結果には approval: false が付き、段に進んだ請求書は stage と digest を添えます。利用側が用意する port は taxFacts（G-07）・render.transform（J-01）・store.submit（C-05）・ledger.append と state（H-01）です。副作用は束ねた継手が起こし（EXTERNAL_IRREVERSIBLE）、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-05",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "サブスク・継続課金 — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、定期課金の要求（SubscriptionBillingRequest）の請求（BILL_CYCLE）・一時停止（PAUSE）・再開（RESUME）・解約（CANCEL）を、pin した一覧と、お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（SubscriptionBillingReceipt）を照合する継手（つぎて）です。外れれば送らず、受理しません（fail-closed）。",
        "problem": "定期課金を外の決済基盤へ運ぶとき、許していない請求や再開を送ってしまうこと、同じ請求を二度送ってしまうこと、op と合わない状態の receipt やお金が動いたか分からない receipt を信じてしまうことを防ぎます。",
        "use_when": [
          "定期課金の請求（BILL_CYCLE→BILLED）・一時停止（PAUSE→PAUSED）・再開（RESUME→RESUMED）・解約（CANCEL→CANCELLED）を外の決済基盤へ運び、receipt が op に合う状態かを照合したいとき。",
          "お金が動く操作（請求・再開）を、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
          "請求に冪等キーを必須にし、同じキーの請求を重複（DEDUP_HIT、新しい効果なし）として扱いたいとき。",
          "決済基盤の障害のあとに再送するとき、照会（lookup）で解いて二度動かさないようにしたいとき。"
        ],
        "do_not_use_when": [
          "請求できたことを、業務の承認として扱いたいとき。定期課金の成功は業務の承認ではありません（business_approval は運びから導きません）。",
          "額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません（外の判断と人・組織の役目です）。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
          "冪等キーを検めます。冪等キーの無い請求（BILL_CYCLE）、同じキーで別の中身の要求は送りません。",
          "重複なのに効果を名乗る receipt は受理しません。重複（DEDUP_HIT）の効果は NONE です。",
          "pin した一覧を検めます。pin していない支払い方法、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
          "業務の承認を名乗る receipt は受理しません。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "定期課金の成功は業務の承認ではありません。どの結果にも approval: false が付きます。業務の承認は核（approval-facts）と人・組織の役目です。",
          "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に SubscriptionBillingRequest を渡すと、DONE（result に billing_state・business_approval）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: 定期課金の操作は EXTERNAL_IRREVERSIBLE、重複・辞退・失敗などは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-06",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "支払・送金（payout） — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、支払い（payout）の要求（PayoutRequest）を、人・組織から受け取った根拠の参照・pin した支払い先・お金が動く操作を許す設定（allow_money_movement）で検めてから決済基盤へ運び、戻った receipt（PayoutReceipt）を契約どおりに照合する継手（つぎて）です。ERABAZU は支払いの許可を作らず、根拠が無ければ送りません（fail-closed）。",
        "problem": "事業者への支払いを外の決済基盤へ運ぶとき、人・組織の根拠が無いまま送ってしまうこと、pin していない支払い先へ送ってしまうこと、支払いの許可を名乗る receipt を信じてしまうことを防ぎます。支払えることを支払いの許可と取り違えないための検めです。",
        "use_when": [
          "事業者への支払いを、人・組織が出した根拠を recordAuthorityEvidence(evidence_ref, origin) で受け取った後だけ運びたいとき（origin は HUMAN か ORGANIZATION だけ。この操作はチャージバックの受け入れの根拠も受け取ります）。",
          "支払い先を、pin した一覧（payout_destinations）に限りたいとき。",
          "支払いを、設定 allow_money_movement: true で明示的に許したときだけ送りたいとき（既定は送りません）。",
          "同じ支払いを二度動かしたくないとき。冪等キーを必須にし、同じ request_id は二度送らず、障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "この継手に支払いの許可を出させたいとき。ERABAZU は支払いの許可を作りません。支払いの実行は人の役目です（Kernel と同じ承認の境界）。",
          "根拠が本物か、額・通貨・税・手数料が正しいかを確かめたいとき。この継手はどちらも確かめません（外の判断と人・組織の役目です）。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "根拠を検めます。根拠の参照が無い支払い、recordAuthorityEvidence で受け取っていない根拠を使う支払い、origin が ERABAZU の根拠は送りません。",
          "支払いの許可を名乗る receipt は受理しません。",
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、お金が動く操作は送りません（REFUSED）。",
          "pin した一覧を検めます。pin していない支払い先、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "冪等キーを検めます。冪等キーの無い支払い、同じキーで別の中身の要求は送りません。",
          "receipt を照合します。op と合わない状態、辞退なのに効果を名乗る receipt、外への効果を増やす receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "お金が動いたか分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。人の確認へ回します。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "支払えたことは支払いの許可ではありません。result の payout_authorization も運びから導かず、どの結果にも approval: false が付きます。",
          "支払いの許可の中身（本物の根拠か）、額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で根拠を受けた後、submit(request) に PayoutRequest を渡すと、DONE（result に payout_state・payout_authorization）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-07",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "税計算 — 税計算の部品（Kernel）",
        "summary": "税計算の部品（Kernel）は、日本の消費税（標準税率 10%・軽減税率 8%・旧税率 8%）について、適格請求書（または適格簡易請求書）1 枚の明細から、税率ごとの対価の合計と消費税額等、記載事項の欠けを事実として返す単独の部品です。税率はコードに持たず、渡された税率の表（期間と国・地方の内訳つき）から選び、端数処理の方法は事業者の設定から請求書の日付で選びます。表や設定で決まらないものは推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "「税込」の裏で、税額を明細ごとに丸めてしまうこと、浮動小数点の誤差が税額に入ること、コードに持った税率で計算してしまうこと、表や設定で決まらない入力を推測で埋めてしまうことを防ぎます。税額の端数処理は、1 枚につき税率ごとに 1 回だけです。",
        "use_when": [
          "適格請求書（QUALIFIED）か適格簡易請求書（SIMPLIFIED）1 枚の明細（1〜1000 件）から、税率ごとの対価の合計と消費税額等を事実として出したいとき。",
          "税額の端数処理を、1 枚につき税率（率の内訳 rate・national_rate・local_rate の組）ごとに 1 回だけ、事業者の設定の方法（FLOOR 切捨て・CEIL 切上げ・HALF_UP 四捨五入）で行いたいとき。明細ごと・表の行ごとには丸めません。",
          "税抜（TAX_EXCLUDED）か税込（TAX_INCLUDED）か（1 枚に 1 つ）に応じて、税額を浮動小数点を使わず正確な分数で出したいとき。金額と率は 10 進の文字列で渡します。",
          "税率を、使う側が渡す税率の表（行ごとの期間・国と地方の内訳・出典つき）から選びたいとき。同梱の表は bundledTaxTable() が写しを返し、ほかの表を tax_table として渡すこともできます。",
          "記載事項の欠け（氏名又は名称・登録番号・取引年月日・取引の内容・軽減税率の対象である旨など）を、計算と同じ呼び出しで一覧にしたいとき。欠けがあっても計算は止めません。",
          "同じ請求書から、キーの並びに関係なく同じ invoice_digest（RFC 8785 の正準 JSON の sha256）と、byte 単位で同じ出力が要るとき。時計・乱数・環境の locale を使いません。"
        ],
        "do_not_use_when": [
          "期間の集計（売上税額・仕入税額の積上げ計算、割戻し計算との選択）、簡易課税のみなし仕入率、2割特例、輸入消費税が要るとき。この部品は請求書 1 枚の中で閉じる計算だけです。",
          "非課税・不課税・免税の判定、源泉徴収、ほかの国の税が要るとき。カタログの一言「消費税・源泉等」のうち、源泉徴収は含みません。",
          "品目が軽減税率の対象かどうか、経過措置が当たるかどうかを判定させたいとき。呼び出し側が rate_class と rate_id の名指しで決めて渡します。",
          "登録番号が国税庁の公表サイトの登録簿にあるかを確かめたいとき。T と 13 桁の数字の形だけを検め、登録簿には照会しません。",
          "負の金額（値引き・返品・対価の返還等）を含む請求書を扱うとき。v1 は負の金額を受け取らず、NEGATIVE_AMOUNT で止めます。",
          "ほかの国の通貨の金額を扱うとき。UNSUPPORTED_CURRENCY で止めます。",
          "税額の国・地方への分け方が要るとき。国と地方の内訳は率だけを出します。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が { invoice, tax_table, taxpayer_profile } の 3 つだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキーは受け取らず、素のデータに写してから検めます。",
          "請求書の形が違うとき（知らないキー・足りないキー・語彙の外の値・単独のサロゲート・明細が 0 件か 1000 件を超える・reduced_marker が true 以外）INVALID_INVOICE、日付が実在する暦の日でないとき INVALID_DATE、同じ line_id の明細が 2 つあるとき DUPLICATE_LINE_ID で止めます。",
          "金額が { value, scale, currency } の決まった形でないとき（scale が 0〜6 の整数でない・小数の桁数が scale と違う）MALFORMED_AMOUNT、金額が負のとき NEGATIVE_AMOUNT、通貨が違うとき UNSUPPORTED_CURRENCY で止めます。",
          "税率の表の形が違うとき（出典の url が https でない場合を含む）INVALID_TAX_TABLE、行に出典が無いとき MISSING_SOURCE、率が決まった形でない・0 のとき MALFORMED_RATE、national_rate + local_rate が rate と等しくないとき RATE_SPLIT_MISMATCH で止めます。",
          "期間の終わりが始まりより前のとき INVALID_PERIOD、同じ rate_id の行が 2 つあるとき DUPLICATE_RATE_ID、同じ区分の行の期間が重なるとき OVERLAPPING_RATE_ROWS、事業者の設定の形が違うとき（端数処理の方法が FLOOR・CEIL・HALF_UP 以外など）INVALID_PROFILE で止めます。",
          "取引の日が無いとき、取引の日に当たる行が表に無いとき、名指しの rate_id が表に無い・区分が違う・日が合わないとき、請求書の日付に当たる端数処理の設定が 0 件か 2 件以上のときは、推測せず need_input にし、groups を空、invoice_totals を null にします（途中までの計算を出しません）。",
          "記載事項（消法57の4①②、インボイスQ&A 問54・問58）のうち、氏名又は名称・登録番号（形だけ）・取引年月日・取引の内容と軽減税率の対象である旨・交付を受ける事業者の名称（適格請求書だけ）を入力の欄から、税率ごとの合計・適用税率・税額を載せるかを presentation の申告から検め、無いものを required_items の missing に並べます。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは税の事実だけです。inference と advice はいつも null で、出力のどの文字列にも直し方や判断の語を入れません。approval は false、authority は NOT_DERIVED です（derives_authority: false）。",
          "結果は事実の提示であって、税務上の判断ではなく、申告が正しいことを示すものでもなく、税務の判断や申告の代行でもありません。税務相談は税理士の独占業務です。経過措置の当否・品目の区分・登録番号が登録簿にあるかは、この部品の外で確かめます。",
          "期間の集計（積上げ計算・割戻し計算の選択の当否）、簡易課税、2割特例、輸入消費税、非課税・不課税・免税の判定、源泉徴収、ほかの国の税は扱いません。",
          "標準税率の明細に reduced_marker が付いていることは検めません（記載事項の欠けとしても出しません）。sales_tax_method・taxation_method は形を検めて写すだけで、計算には使いません。",
          "税込で通貨の最小単位未満を含む金額を渡し、切上げ・四捨五入で税額が合計を超えると、税抜の合計は負の値になります。そのまま事実として出し、直しません。",
          "同梱の表は 2026-10-01 時点の国税庁の資料による 3 行だけです。2014-04-01 より前の税率と、成立していない改正案は入っていません。表に行の無い期間の取引の日は need_input になります。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/tax-facts.js の computeInvoiceTax({ invoice, tax_table, taxpayer_profile }) を呼びます（例外を投げません）。受け取るのは請求書（明細 lines・presentation など）、税率の表（行ごとの率の内訳・期間・出典）、事業者の設定（rounding）です。返すのは { ok: true, errors: [], facts }（groups・invoice_totals・required_items・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, where }] }（where は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-08",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "為替レートの取得 — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、為替レートの要求（FxRateRequest）を、pin した通貨の組（fx_pairs）とレートの出どころ（rate_sources）で検めてから運び、戻った receipt（FxRateReceipt）を契約どおりに照合する継手（つぎて）です。観測したレートは権威ではなく、権威を名乗る receipt は受理しません（fail-closed）。",
        "problem": "為替レートを外から取ってくるとき、pin していない通貨の組や出どころのレートを使ってしまうこと、観測したレートを権威として扱ってしまうことを防ぎます。",
        "use_when": [
          "pin した通貨の組と出どころに限って為替レートを観測して運び、結果（rate_state・rate_ref）として受け取りたいとき。",
          "受け取ったレートを、権威ではない観測値として扱う前提で使いたいとき（rate_authority は運びから導きません）。"
        ],
        "do_not_use_when": [
          "通貨の変換（換算）をしたいとき。この継手は為替レートの取得（観測したレートの運搬）の継手で、換算の機能は含みません。",
          "観測したレートを、権威のあるレートとして扱いたいとき。観測したレートは権威ではありません。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin した一覧を検めます。pin していない通貨の組、pin していないレートの出どころ、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "権威を名乗るレートの receipt は受理しません。",
          "外への効果を増やす receipt は受理しません。為替レートの要求の効果は NONE です。",
          "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "為替レートの要求を運ぶ継手で、換算（通貨の変換）の機能は含みません。",
          "観測したレートは権威ではありません。どの結果にも approval: false が付きます。",
          "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に FxRateRequest を渡すと、DONE（result に rate_state・rate_ref・rate_authority）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-09",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "突合用の明細取り込み（processor） — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、明細の取り込みの要求（StatementIntakeRequest）を、pin した明細の出し手（processors）で検めてから運び、戻った receipt（StatementIntakeReceipt）を契約どおりに照合する継手（つぎて）です。明細を運んでも突き合わせではなく、突き合わせを名乗る receipt は受理しません（fail-closed）。",
        "problem": "決済の明細を外から取り込むとき、pin していない出し手の明細を取り込んでしまうこと、明細を運んだことを明細と入金の突き合わせが済んだことと取り違えることを防ぎます。",
        "use_when": [
          "pin した明細の出し手に限って明細を取り込んで結果（intake_state）として受け取り、突き合わせは核（reconcile-facts）や人・組織に任せたいとき。",
          "取り込みと突き合わせを分けて扱いたいとき。突き合わせを名乗る receipt は受理せず、reconciliation は運びから導きません。"
        ],
        "do_not_use_when": [
          "明細と入金の突き合わせをしたいとき。この継手は突き合わせをしません。明細の突き合わせは外の判断と人・組織の役目です。",
          "明細の額・通貨・税・手数料の正しさを確かめたいとき。この継手は確かめません。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin した一覧を検めます。pin していない明細の出し手、空・パス・.. を含む参照は送りません。空の一覧は何も許しません。",
          "突き合わせを名乗る receipt は受理しません。",
          "外への効果を増やす receipt は受理しません。明細の取り込みの効果は NONE か EXTERNAL_REVERSIBLE（FAILED は NONE）です。",
          "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "明細の突き合わせはしません。明細を運んでも突き合わせではなく、result の reconciliation も運びから導きません。",
          "取り込めたことは承認ではありません。どの結果にも approval: false が付きます。",
          "額・通貨・税・手数料の正しさは確かめません（外の判断と人・組織の役目です）。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の submit(request) に StatementIntakeRequest を渡すと、DONE（result に intake_state・reconciliation）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 決済基盤への port（submit、任意で lookup）。副作用: NONE か EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-10",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "チャージバックの申し立て — 決済の運び継手（Tsugite）",
        "summary": "決済の運び継手（Tsugite）は、チャージバックの要求（DisputeRequest）の申し立て（OPEN）・証拠の提出（SUBMIT_EVIDENCE）・観測（OBSERVE）・受け入れ（ACCEPT）を検めてから運び、戻った receipt（DisputeReceipt）が op どおりの状態かを照合する継手（つぎて）です。受け入れは、人・組織の根拠を受け取った後で、お金が動く操作として設定で許したときだけ送ります。運んでも解決の権限ではありません（fail-closed）。",
        "problem": "チャージバックを外の決済基盤へ運ぶとき、証拠の参照の無い提出を送ってしまうこと、人・組織の根拠の無いまま受け入れ（ACCEPT）を送ってしまうこと、解決の権限を名乗る receipt を信じてしまうことを防ぎます。受け入れは取り消せない業務の判断なので、根拠があるときだけ運びます。",
        "use_when": [
          "チャージバックの申し立て（OPEN→OPENED）・証拠の提出（SUBMIT_EVIDENCE→EVIDENCE_SUBMITTED）・観測（OBSERVE→OBSERVED）・受け入れ（ACCEPT→ACCEPTED）を運び、receipt が op に合う状態かを照合したいとき。",
          "チャージバックの解決を人・組織の判断に残し、運びからは解決の権限（resolution_authority）を導かないようにしたいとき。",
          "チャージバックの受け入れ（ACCEPT）を、人・組織が出した根拠を recordAuthorityEvidence で受け取った後だけ、設定 allow_money_movement: true で許したときだけ運びたいとき。"
        ],
        "do_not_use_when": [
          "人・組織の根拠が無いまま、または設定 allow_money_movement が true でないまま、チャージバックを受け入れたい（ACCEPT）とき。この継手は送りません（REFUSED）。",
          "チャージバックを解決したいとき、または返金をしたいとき。この継手はチャージバックの申し立て・証拠の提出・観測・受け入れを運ぶ継手で、解決の権限は無く、返金の機能は含みません。受け入れを運んでも解決ではありません。",
          "受け入れの根拠が本物の判断かを確かめたいとき。この継手は、人・組織から受け取った参照かだけを見ます。",
          "すぐに実の決済基盤につなぎたいとき。実の決済基盤・PSP・銀行には一度も接続しておらず、port の実装も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "受け入れの根拠を検めます。根拠の参照が無い受け入れ（ACCEPT）、recordAuthorityEvidence で受け取っていない根拠を使う受け入れ、origin が ERABAZU の根拠の受け入れは送りません。受け入れ以外の op に根拠の欄を付けた要求も送りません。",
          "お金を動かす許可を検めます。allow_money_movement が true でないとき、または真偽値でないとき、受け入れ（ACCEPT）は送りません（REFUSED）。",
          "証拠の参照の無い証拠の提出は送りません。参照が空・パス・.. を含むものも送りません。",
          "解決の権限を名乗る receipt は受理しません。",
          "外への効果を増やす receipt は受理しません。効果は、観測は NONE、申し立て・証拠の提出は EXTERNAL_REVERSIBLE、受け入れは EXTERNAL_IRREVERSIBLE（FAILED は NONE）です。",
          "op と合わない状態の receipt、別の要求の receipt（request_digest の偽り）は受理せず、以後は停止します（HALTED）。",
          "効果が分からない receipt（効果 UNKNOWN）は受け取らず、以後は停止します（HALTED）。",
          "同じ request_id は二度送りません。決済基盤の障害は UNKNOWN とし、再送は照会（lookup）で解いて、二度動かしません。"
        ],
        "what_it_does_not_do": [
          "チャージバックの解決はしません。受け入れ（ACCEPT）を運んでも解決の権限ではなく、どの結果にも approval: false が付きます。解決は外の判断と人・組織の役目です。",
          "受け入れの根拠の中身（本物の判断か）は確かめません。人・組織から受け取った参照かだけを見ます。ERABAZU は争いを認める判断を作りません。",
          "実の決済基盤・PSP・銀行には一度も接続していません。実の課金・送金は 0 回です。port の実装、pin する一覧、お金を動かす許可は、接続するときに別の関門で決めます。",
          "決済基盤の認証に使う秘密は持ちません。秘密は port 側が持ちます（HELD_BY_PORT）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の決済サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/payment-rails/v1 でつなぎます。createPaymentRailsClient({ cfg, port }) の recordAuthorityEvidence で根拠を受けた後、submit(request) に DisputeRequest を渡すと、DONE（result に dispute_state・resolution_authority）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: port。副作用: 観測は NONE、申し立て・証拠の提出は EXTERNAL_REVERSIBLE、受け入れは EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-11",
        "category": "G/決済・資金",
        "kind": "Primitive",
        "title": "金額の 10 進表現 — 金額の 10 進表現（文字列＋scale）の部品（Primitive）",
        "summary": "金額の 10 進表現（文字列＋scale）の部品（Primitive）は、金額を { value, scale, currency }（10 進の整数の文字列・小数点の位置・通貨のコード）で持ち、10 進の文字列からの読み取り、末尾の 0 を削る正規化、同じ通貨の足し算・比較・等しさ、10 進の文字列への書き出しをする単独の部品です。計算は BigInt だけで、浮動小数点を使わず、丸めません。",
        "problem": "小数の誤差で、金額が端数の分だけずれる事故を防ぎます。浮動小数点を使わないので誤差が入らず、38 桁に収まらない結果は黙って丸めずに止めます。受け取れない値は、決まったコードと位置で止めます。",
        "use_when": [
          "10 進の文字列（-?[0-9]+(.[0-9]+)?、ASCII の数字だけ）を、通貨のコードとあわせて金額に読みたいとき（parseDecimal）。scale は書かれた小数の桁数のままで、正規化しません。",
          "末尾の 0 を削り、scale を最小にした金額にそろえたいとき（normalize。0 は { value: \"0\", scale: 0 }）。",
          "同じ通貨の 2 つの金額を、丸めずに足したいとき（add。結果は正規化した金額）。",
          "scale によらず、数の大小（compare の -1・0・1）や等しさ（equal）を知りたいとき。equal は通貨が違えば false で、エラーではありません。",
          "金額を、小数の桁数がちょうど scale の 10 進の文字列に書き出したいとき（toText）。parseDecimal(toText(a)) は同じ value・scale に戻ります。",
          "時計・乱数・環境の locale・外の接続を持たない、決定的な処理として手元で呼びたいとき。"
        ],
        "do_not_use_when": [
          "丸め・掛け算・割り算・為替の換算が要るとき。この部品は扱いません。",
          "記号や桁区切りの付いた表示用の文字列が要るとき。表示用の整形は L-03 の部品の役目です。",
          "通貨のコードが ISO 4217 の表にあるか、通貨ごとの小数の桁数に合うかを確かめたいとき。この部品は形（英大文字 3 字）だけを検めます。",
          "38 桁・scale 18 を超える金額を扱うとき。表せないので止めます（TOO_MANY_DIGITS・INVALID_SCALE）。",
          "整数部の先頭の 0 を断りたいとき。parseDecimal は先頭の 0 を受け取って捨てます。より狭い形が要るときは、使う側で検めます。",
          "text の長さの上限が要るとき。上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が素のオブジェクトでない、決めたキーの外を持つ、getter・列挙しないプロパティ・symbol のキーを持つ、値を調べること自体が例外になるとき（Proxy の罠など）、INVALID_ARGUMENT で止めます。getter は呼びません。",
          "text・value が文字列でないとき（数として渡された値・NaN・Infinity・bigint・無いキー）、NOT_A_STRING で止めます。",
          "text が -?[0-9]+(.[0-9]+)? の形でないとき（指数・+・空白・桁区切り・\"1.\"・\".5\"・全角の数字・改行）、INVALID_DECIMAL_TEXT で止めます。",
          "value が -?[0-9]+ の形でない・先頭に余分な 0 があるとき INVALID_VALUE、\"-0\" などの負の 0 のとき NEGATIVE_ZERO で止めます。",
          "桁が 38 を超えるとき（足し算の結果を含む）TOO_MANY_DIGITS、scale が 0〜18 の整数でないとき（-0・小数・文字列）や小数の桁数が 18 を超えるとき INVALID_SCALE で止めます。",
          "currency が英大文字 3 字の文字列でないとき INVALID_CURRENCY、add・compare の 2 つの金額の通貨が違うとき CURRENCY_MISMATCH で止めます。",
          "止めたときは、決まった順で最初の 1 つだけを、コード（code）と位置（where。JSON Pointer で、2 つの金額の操作では /a・/b から始まる）で返します。"
        ],
        "what_it_does_not_do": [
          "丸め・掛け算・割り算・為替の換算・表示用の整形はしません。",
          "計算できたことは、金額が業務として正しいことでも、受け入れてよいことでもありません。",
          "通貨ごとの小数の桁数には照らしません。ISO 4217 の表に無いコードでも、形が合えば受け取ります。",
          "全角・ほかの文字体系の数字は受け取りません。数字は ASCII の 0〜9 だけです。",
          "計算の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の 10 進数のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/decimal-amount.js の parseDecimal({ text, currency })・normalize(amount)・add(a, b)・compare(a, b)・equal(a, b)・toText(amount) を呼びます（どれも投げません）。金額は { value, scale, currency }（value は符号を除いて 38 桁までの整数の文字列、scale は 0〜18 の整数、currency は英大文字 3 字）です。返すのは { ok: true, errors: [], amount | result | equal | text } か { ok: false, errors: [{ code, where }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-12",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "取引の事象 ID — 取引の事象 ID（txn_id）の部品（Kernel）",
        "summary": "取引の事象 ID（txn_id）の部品（Kernel）は、取引・証憑の事実（kind・source・occurred_on・amount・counterparty）から、決まった規則で事象の ID（txn_id）を出す単独の部品です。ハッシュする形を RFC 8785 の正準 JSON にして SHA-256 をとり、先頭 16 bytes を小文字の hex 32 文字にします。必要な事実が無いときは推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "二重計上を防ぐ照合の鍵には、同じ事実からいつも同じ値が要ります。金額の末尾の 0 と scale の書き方の違い、入力のキーの順、浮動小数点の誤差、raw_refs のような出どころの参照によって、同じ事実の ID が変わってしまうことを防ぎます。",
        "use_when": [
          "取引・証憑の事実から、二重計上を防ぐ照合の鍵として、同じ事実から同じ値になる事象の ID（txn_id）を作りたいとき。",
          "同じ値の金額を、scale の書き方によらず同じ ID にしたいとき。金額は BigInt で末尾の 0 を削って scale を最小にし、浮動小数点を使いません。通貨・符号・値が違えば別の ID です。",
          "取引先を、出どころの ID（id_in_source）か名前で表したいとき。id_in_source があって空でなければそれだけを使い、無ければ名前を NFKC にして前後の空白を削ったものを使います。",
          "必要な事実が欠けているときに、推測で埋めず、無い事実の位置（JSON Pointer）の一覧 missing を受け取りたいとき。state は need_input で、エラーではありません。",
          "ID と一緒に、ハッシュした形（hashed）と、その正準 JSON の文字列（canonical）も受け取り、ID の元を確かめたいとき。",
          "時計・乱数・環境の locale を使わず、入力のキーの順によらない、決定的な出力が要るとき。外の境界・状態はありません。"
        ],
        "do_not_use_when": [
          "2 つの記録が同じ取引かどうかを判断させたいとき。同じ欄の値を持つ別々の取引（同じ日・同じ金額・同じ取引先の 2 回の買い物など）は同じ txn_id になります。区別は source_key の役目で、G-13・G-14 の部品が扱います。",
          "表記のゆれを寄せたいとき。大文字と小文字、(株) と 株式会社、ゼロ幅の文字、空白の数の違いは別の ID になります。別名の候補を出すことは、この部品に含みません。",
          "kind（evidence か transaction か）・source・id_in_source を決めさせたいとき。どれも呼び出し側が決めて渡します。kind は申告されたものを使い、判定しません。",
          "記録の形の検め・supersedes の扱い・重複のまとまり分けが要るとき。記録の形と supersedes は G-13、同一性の基準は G-14 の部品の範囲です。",
          "ID が衝突しないことを前提にしたいとき。txn_id は 128 bit に切り詰めたハッシュで、衝突しないことは約束しません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は Node.js（ICU）の String.prototype.normalize によるため、Unicode の版が変わると結果が変わりえます。"
        ],
        "what_it_checks": [
          "引数が 6 つのキー（kind・source・occurred_on・amount・counterparty・raw_refs）だけを持つ素のオブジェクトでないとき、getter・列挙しないプロパティ・symbol のキーを持つとき（raw_refs を除く）、値を調べること自体が例外になるとき（Proxy の罠など）は INVALID_ARGUMENT で止めます。",
          "kind が evidence・transaction でないとき INVALID_KIND、source が ^[a-z0-9][a-z0-9_-]{0,62}$ の文字列でないとき INVALID_SOURCE、occurred_on が YYYY-MM-DD の実在する暦の日（0001〜9999 年）でないとき INVALID_DATE で止めます。",
          "amount が { value, scale, currency } だけを持つ素のオブジェクトでないとき、value が -?[0-9]+ の形でない（先頭の 0・\"-0\"・符号を除いて 38 桁を超える）とき、scale が 0〜18 の整数でない（-0 を含む）とき、currency が英大文字 3 字でないときは INVALID_AMOUNT で止めます。",
          "counterparty が { id_in_source, name } のうちだけを持つ素のオブジェクトでないとき、どちらかが文字列か null でないとき、単独のサロゲートを含むときは INVALID_COUNTERPARTY で止めます。id_in_source があるときも、名前の形は検めます。",
          "形の違う欄が 1 つでもあれば、無い欄があってもエラー（{ ok: false }）にし、最初に見つけた 1 つだけを返します。検める順は 引数の形 → kind → source → occurred_on → amount → counterparty です。",
          "raw_refs は読まず（形も検めず、getter も呼ばず）、ID に入れません。検めた値とハッシュする値は同じです。",
          "結果を、src の補助関数も src/canonical.js も crypto も使わずに規則から書き直した独立オラクル（SHA-256 も純 JS）と突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC だけはオラクルも同じ関数を使います。",
          "15 件の既知の答え（test/known-answers.json）で試験しています。正準の文字列は規則から手で書き、txn_id は src を使わない純 JS の SHA-256 で求めたものです。G-13 の部品と同じ答えを共有します。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、出力のどの文字列にも判断の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "事象 ID は決まった欄から決まった規則で作った値で、記帳や突合の判断ではありません。2 つの記録が同じ取引かどうかは決めません。",
          "id_in_source は正規化しません（前後の空白も含めてそのまま使います）。名前は NFKC と前後の空白の削りだけで、それ以上は寄せません。",
          "通貨のコードは形（英大文字 3 字）だけを検め、ISO 4217 の表とは照らしません。",
          "id_in_source と name の長さの上限は持ちません。NFKC・正準化・ハッシュには長さに比例する時間がかかります。大きさの上限は I-06 の部品か使う側で決めます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/txn-id.js の computeTxnId(input) を呼びます（例外を投げません）。受け取るのは { kind, source, occurred_on, amount: { value, scale, currency }, counterparty: { id_in_source?, name? }, raw_refs? } です。返すのは { ok: true, errors: [], facts }（state・txn_id・hashed・canonical・missing、inference: null・advice: null）か { ok: false, errors: [{ code, where }] }（where は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-13",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "取引・突合の記録の検め — 共通形の取引・突合の記録の検めの部品（Kernel）",
        "summary": "共通形の取引・突合の記録の検めの部品（Kernel）は、共通形の取引（erabazu.common.txn/1）の欄をすべて検め、txn_id が G-12 の事象 ID の規則で計算し直した値と同じかを確かめる単独の部品です。突合（erabazu.common.match/1）は取引とは別の記録として検めます。取引の集合について supersedes の鎖の事実と古い順の鎖を出しますが、どの版が正しいかは決めません。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "帳簿の元になる記録の形は、誰も見ないまま使われがちです。記録に書かれた txn_id を言い分のまま信じること、突合の情報を取引の記録に書き込んでしまうこと、supersedes の鎖の切れ・循環・枝分かれに気づかないことを防ぎます。",
        "use_when": [
          "共通形の取引 1 件を、閉じた形（schema・txn_id・kind・occurred_on・amount・counterparty・lines・source・raw_refs・probe_version・extracted_at と、任意の supersedes・source_key）として検めたいとき（checkTxn）。",
          "記録の txn_id が、kind・source・occurred_on・amount・counterparty から G-12 の規則で計算した値と同じかを確かめたいとき。違えば TXN_ID_MISMATCH と、計算し直した値 recomputed_txn_id を返します。",
          "突合の記録（evidence_txn・transaction_txn・basis・matcher_version・confidence）を、取引とは別の記録として検めたいとき（checkMatch）。取引の欄を持つ突合の記録は受け取りません。",
          "取引の集合（10,000 件まで）について、supersedes の鎖を古い順に並べ、相手が無い・同じ id・別の source・循環・枝分かれを problems として受け取りたいとき（checkSupersedesChain）。",
          "時計・乱数・環境の locale を使わない、決定的な出力が要るとき。checkSupersedesChain の結果は入力の並びにもよりません。外の境界・状態はありません。"
        ],
        "do_not_use_when": [
          "どの版が正しいか・どれを使うかを決めさせたいとき。supersedes の鎖の事実を並べるだけです。",
          "評価（為替の換算など）や、重複のまとまり分けが要るとき。まとまり分けは G-14 の部品の範囲です。",
          "突合の evidence_txn・transaction_txn が実在する取引か、それぞれの kind が合っているかを確かめたいとき。id の形と 2 つが違うことだけを検めます。",
          "明細の金額の合計が取引の金額と合うか、明細の通貨が取引の通貨と同じかを確かめたいとき。どちらも検めません。",
          "欠けた事実を集めたいとき。記録の欄が足りなければ need_input ではなく INVALID_RECORD で止めます。この部品は保存された記録の検めで、無い事実の一覧を出す G-12 とは役割が違います。",
          "中身を直しても txn_id が変わらない訂正を、同じ集合で扱いたいとき。同じ txn_id が 2 件ある集合は DUPLICATE_TXN_ID で止まります。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は実行環境の ICU によるため、Unicode の版が変わると名前の正規化の結果が変わることがあります。"
        ],
        "what_it_checks": [
          "記録が素のデータでないとき（getter・クラスのインスタンス・穴のある配列・symbol のキー・__proto__ のキー・入れ子 8 段や配列 10,000 要素を超える）、知らないキー・足りないキーがあるとき INVALID_RECORD、値を調べること自体が例外になるとき INSPECTION_FAILED で止めます。",
          "schema が違うとき INVALID_SCHEMA、txn_id・supersedes などが小文字の hex 32 文字でないとき INVALID_TXN_ID、kind が語彙の外のとき INVALID_KIND、日付が実在する暦の日でないとき INVALID_DATE、金額の形が違うとき INVALID_AMOUNT で止めます。",
          "取引先が空（id_in_source が空で、NFKC と trim の後の name も空）か形が違うとき INVALID_COUNTERPARTY、明細・数量の形が違うとき INVALID_LINES、source の形が違うとき INVALID_SOURCE、raw_refs が 1〜1024 件の重複のない Ref の配列でないとき INVALID_RAW_REFS で止めます。",
          "probe_version の形が違うとき INVALID_PROBE_VERSION、extracted_at が RFC 3339 の日時でない（うるう秒・小文字の t と z を含む）とき INVALID_TIME、source_key の形が違うとき INVALID_SOURCE_KEY で止めます。検めは決まった順で、最初に当たった 1 つだけを返します。",
          "txn_id を記録の言い分のまま信じず、G-12 の規則で計算し直して比べます。違えば TXN_ID_MISMATCH で止めます。",
          "突合の記録が素のデータでない・知らないキー（取引の欄を含む）・足りないキーがあるとき、basis・matcher_version・confidence（exact・probable・manual）の形が違うとき INVALID_MATCH、2 つの id が同じとき SAME_TXN_MATCHED で止めます。",
          "鎖の引数が { txns } だけの素のオブジェクトでないとき INVALID_ARGUMENT、同じ txn_id が 2 件あるとき DUPLICATE_TXN_ID で止めます。問題のある辺（相手が無い・同じ id・別の source・枝分かれ・循環）は鎖につながず、SUPERSEDES_ で始まるコードの事実として problems に出します。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC と SHA-256 は、オラクルも Node.js の同じ関数を使います。"
        ],
        "what_it_does_not_do": [
          "返すのは記録の形と規則との一致の事実だけです。inference と advice はいつも null、approval は false で、権限も作りません（derives_authority: false）。入力の名前・説明の文字列はそのまま写します。",
          "結果は、取引の中身が正しいこと・どの版が正しいことの判断ではありません。会計の判断や記帳の代行でもありません。突合の当否・kind の当否・金額の評価（為替など）は、この部品の外です。",
          "kind（evidence か transaction か）は宣言をそのまま受け取り、判定しません。",
          "raw_refs が指す取得物が実在するか、probe_version が実在する版かは検めません（形だけ）。通貨の英大文字 3 字も形だけを検め、ISO 4217 の一覧とは照らしません。",
          "G-12 の部品そのものは読み込まず、規則の写しを中に持ちます。写しが同じことは、両方の既知の答えを試験で照らすことと、一度だけの突き合わせで確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/txn-record.js の checkTxn(record)・checkMatch(record)・checkSupersedesChain({ txns }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts } か { ok: false, errors: [{ code, where }] }（where は JSON Pointer。TXN_ID_MISMATCH は recomputed_txn_id も持つ）です。checkTxn の facts は txn_id・hashed・canonical・supersedes・source_key、鎖では chains と problems です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "G-14",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "取引の同一性の基準 — 取引の同一性の基準の部品（Kernel）",
        "summary": "取引の同一性の基準の部品（Kernel）は、呼び出し側が宣言した同一性の基準（source_key か、日付・金額・取引先から選んだ composite_key）に従って、取引の記録を同じ鍵のまとまりに分ける単独の部品です。宣言した鍵が無い記録は推測せず need_input に並べます。まとまりの中で欄が違えば differing: true にするだけで、統合も削除もしません。曖昧一致はしません。inference と advice はいつも null です。",
        "problem": "「同じ取引か」の判断の土台として、宣言した基準で同じ鍵になる記録をまとめます。鍵の無い記録を推測で埋めること、似た名前・近い日付・近い金額を同じものとして寄せてしまうこと、まとめるときに記録の違いを捨ててしまうことを防ぎます。",
        "use_when": [
          "記録にプラットフォームの取引 id（source_key）があり、それだけを鍵にまとめたいとき（basis: source_key）。identity_key は \"sk:\" と source_key をつないだ文字列です。",
          "id が無く、日付（occurred_on）・金額（amount）・取引先（counterparty）から 1〜3 個を選んで鍵にしたいとき（basis: composite_key）。鍵は選んだ欄だけの RFC 8785 の正準 JSON の SHA-256（小文字の hex 64 文字）です。",
          "金額の scale の違いや取引先の名前の全角と半角の違いを、G-12 の事象 ID と同じ正規化で同じ鍵にしたいとき。金額は末尾の 0 を削って scale を最小にし、取引先は id_in_source が空でなければそれだけ、無ければ名前を NFKC にして前後の空白を削ります。",
          "宣言した鍵が無い記録を、推測で鍵を作らず need_input として受け取りたいとき。各記録は groups か need_input のどちらかにちょうど 1 回入ります。",
          "同じ鍵のまとまりの中で記録の欄の有無や値が違うことを、differing として知りたいとき。書いたままの値で比べるので、scale・全角と半角のような表現の違いも違いとして数えます。",
          "時計・乱数・環境の locale を使わない、決定的な出力が要るとき。groups は identity_key の順、refs と need_input は入力の順です。"
        ],
        "do_not_use_when": [
          "似た名前・近い日付・近い金額の候補を出したいとき。曖昧一致はしません。取引先の名前は NFKC と前後の空白の除去だけで、それ以上は寄せません。",
          "記録の統合・削除や、どれを残すか（supersedes）を決めさせたいとき。まとまりを出すだけです。",
          "ほかの source の記録を混ぜて、source_key でまとめたいとき。記録に source の欄が無いので、source_key が偶然同じでも区別しません。呼び出し側が source ごとに渡します。",
          "宣言が composite_key のときに source_key も鍵に使いたいとき。宣言に従い、source_key は鍵に使いません（differing には数えます）。",
          "基準を部品に選ばせたいとき。どの基準を使うかは呼び出し側が宣言します。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。NFKC は実行環境の ICU によるため、Unicode の版が変わると名前の正規化の結果が変わることがあります。"
        ],
        "what_it_checks": [
          "引数が { declaration, records } の 2 つのデータプロパティだけの素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキーは受け取りません。",
          "宣言の basis が source_key・composite_key でないとき、知らないキーがあるとき（source_key に fields を付けたものを含む）、fields が occurred_on・amount・counterparty から 1〜3 個の重なりのない配列でないとき INVALID_DECLARATION で止めます。",
          "records が素のデータの配列（10,000 件まで・入れ子 8 段まで）でないとき、記録に知らないキーがある・ref が無いか Ref の形でないとき INVALID_RECORDS、同じ ref の記録が 2 つあるとき DUPLICATE_REF で止めます。",
          "与えられた欄（source_key・occurred_on・amount・counterparty）の形が違うときは、宣言で使わない欄でも INVALID_FIELD で止めます。source_key の空文字も止めます。取引先が { } や空の文字列だけのときは、形の誤りではなく欄が無いもの（need_input）とします。",
          "検めは決まった順（引数 → declaration → 記録ごとの形 → ref の重なり）で、最初に当たった 1 つだけを返します。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。NFKC と SHA-256 は、オラクルも Node.js の同じ関数を使います。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null、approval は false で、権限も作りません（derives_authority: false）。入力の ref・source_key はそのまま写します。",
          "結果は、宣言した基準で同じ鍵になるかの事実で、同じ取引だという判断・どれを残すかの判断ではありません。重複の判断や記帳の代行でもありません。",
          "曖昧な重複の候補出し（suggest-dup）・別名の寄せ（suggest-alias）・統合・削除・supersedes の決定は含みません。",
          "通貨の英大文字 3 字は形だけを検め、ISO 4217 の一覧とは照らしません。",
          "G-12 の部品そのものは読み込まず、正規化の規則の写しを中に持ちます。写しは、Node.js の外の sha256sum で求めた既知の答え 4 件で確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/identity-rule.js の groupByIdentity({ declaration, records }) を呼びます（例外を投げません）。declaration は { basis: \"source_key\" | \"composite_key\", fields? }、records は { ref, source_key?, occurred_on?, amount?, counterparty? } の配列です。返すのは { ok: true, errors: [], facts }（groups・need_input など、inference: null・advice: null）か { ok: false, errors: [{ code, where }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-01",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "改変不能監査ログ — 監査台帳継手（Tsugite）",
        "summary": "監査台帳継手（Tsugite）は、検めた監査イベント封筒（AuditEventEnvelope）だけを追記専用（append-only）の backend へ送り、backend が返す受領（AuditAppendReceipt）を契約の式どおりに照合してから、台帳の先頭を進める継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "監査の記録を外部の追記専用の保管先へ送るとき、型の崩れた封筒や本文の混じった封筒を送ってしまうこと、そして保管先の受領が正しいかを確かめないまま「記録できた」と扱ってしまうことを防ぎます。照合できない追記を信頼しないための検めです。",
        "use_when": [
          "誰が何をしたかを、後から消せない形で残す監査ログを、外部の追記専用 backend に記録したいとき。",
          "backend から返る受領をそのまま信じず、契約の式（連鎖のハッシュ・通番・digest）で照合してから記録済みとしたいとき。",
          "同じイベントを二重に追記したくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
          "受領に不整合が出たら、人が確かめるまで追記を止めたいとき。"
        ],
        "do_not_use_when": [
          "監査イベントの本文そのものを保管したいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
          "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
          "追記の成功を、承認や決裁として扱いたいとき。追記の成功は承認ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。"
        ],
        "what_it_checks": [
          "封筒の型を検めます。追加のフィールド、決まった値（const）と違う値、genesis の規則に反するものは送りません。",
          "pin した台帳を検めます。pin していない台帳への追記は送りません。",
          "連鎖（previous_hash）と通番（sequence）を検めます。前のハッシュの改ざん、通番の巻き戻し・飛びがあれば、送らず、受理しません。",
          "digest を検めます。ハッシュは契約の式（RFC 8785 に制限した正準化と sha256。genesis の previous_hash_value は空文字）で計算し、式と違うものは受理しません。",
          "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません。",
          "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
          "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
          "本文が混じった封筒、大きすぎるイベント、種類を偽った封筒（content-type の偽り）、同じイベントの二重の追記を拒否します。"
        ],
        "what_it_does_not_do": [
          "改変できないかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
          "追記の成功は承認ではありません（approval: false）。",
          "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
          "透明性ログ（Merkle の包含証明）は扱いません。",
          "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "監査 backend の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと、APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false が付きます）。利用側が用意するもの: 追記専用 backend への port（append と、任意で lookup）。pin する endpoint と受領の形式、認証の SecretRef の解決も port 側。副作用は取り消せない外部への書き込み（EXTERNAL_IRREVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-02",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "アクセスログ — 監査台帳継手（Tsugite）",
        "summary": "監査台帳継手（Tsugite）は、「誰が何を見たか」の閲覧の記録を、監査の出来事（AuditEventEnvelope）として追記専用（append-only）の backend へ追記するときに使う継手（つぎて）です。検めた封筒だけを送り、返る受領（AuditAppendReceipt）を照合できたときだけ台帳の先頭を進めます。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "閲覧の記録を外部の追記専用の保管先へ積むとき、型の崩れた封筒や本文の混じった封筒を送ってしまうこと、同じ記録の二重の追記、通番の巻き戻り・飛び、そして保管先の受領が正しいかを確かめないまま「記録できた」と扱ってしまうことを防ぎます。",
        "use_when": [
          "閲覧の記録（誰が何を見たか）を、監査の出来事として、外部の追記専用 backend に 1 件ずつ追記したいとき。",
          "閲覧を捉えて AuditEventEnvelope にする側（利用側）を自分で用意でき、その封筒の追記と照合だけをこの継手に任せたいとき。",
          "backend から返る受領を、契約の式（連鎖のハッシュ・通番・digest）で照合してから、記録済みとしたいとき。",
          "同じ閲覧の記録を二重に追記したくないとき。受領が確定しないまま再送する場合は、照会（lookup）で解きます。",
          "受領に不整合が出たら、人が確かめるまで追記を止めたいとき。"
        ],
        "do_not_use_when": [
          "閲覧そのものを検知・収集したいとき。閲覧を捉えて出来事にするのは利用側の役目で、この継手は追記を運んで照合するだけです。",
          "閲覧した内容そのもの（本文）を台帳に積みたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
          "記録を書き換えたり削除したりしたいとき。この継手は追記だけを扱います。",
          "透明性ログ（Merkle の包含証明）が要るとき。この継手の範囲外です。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "封筒の型を検めます。追加のフィールド、決まった値（const）と違う値、genesis の規則に反するもの、種類を偽った封筒（content-type の偽り）は送りません。",
          "pin した台帳を検めます。pin していない台帳への追記は送りません。",
          "連鎖（previous_hash）と通番（sequence）を検めます。前のハッシュの改ざん、通番の巻き戻し・飛びがあれば、送らず、受理しません。",
          "同じ event_id・同じ内容がもう追記済みなら backend に送らず ALREADY_APPENDED を返し、同じ event_id で内容が違うときは REFUSED を返します。",
          "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません。",
          "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
          "backend の障害（500 の応答や一部だけの書き込み）は UNKNOWN とし、台帳の先頭を進めません。",
          "本文が混じった封筒と、大きさの上限（max_envelope_bytes）を超えるイベントを拒否します。"
        ],
        "what_it_does_not_do": [
          "閲覧を捉えることも、閲覧の出来事を作ることもしません。それは利用側の役目で、この継手は追記を運んで照合するだけです。",
          "追記の成功は承認ではありません（approval: false）。",
          "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
          "透明性ログ（Merkle の包含証明）は扱いません。backend の認証に使う秘密（SecretRef）の解決もしません（port 側の責務です）。",
          "H-02 専用の部品ではありません。H-01（改変不能監査ログ）・C-04（追記専用台帳）・H-08（改ざん検知チェーン）と同じ 1 つの部品が担い、試験と検査も共通です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に、閲覧の記録を表す AuditEventEnvelope を渡すと、APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 閲覧を封筒にする処理と、追記専用 backend への port（append、任意で lookup）。副作用は EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-03",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "同意記録ストア — 監査・プライバシー継手（Tsugite）",
        "summary": "監査・プライバシー継手（Tsugite）は、同意の取得・撤回の記録と問い合わせの要求（ConsentRecord）を、pin した目的と範囲で検めてから同意台帳の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。一つでも外れれば、送らず、受理しません（fail-closed）。同意台帳そのものは含みません。",
        "problem": "同意の取得・撤回を外部の同意台帳へ記録するとき、pin していない目的・範囲での記録、本人のメールアドレスや電話番号をそのまま送ってしまうこと、そして記録できたことを同意の権限（consent authority）と取り違えることを防ぎます。同意の記録は事実で、権限ではありません。",
        "use_when": [
          "同意の取得（RECORD_GRANT）と撤回（RECORD_WITHDRAWAL）の履歴を、外部の同意台帳の基盤に追記したいとき。",
          "記録した同意の状態を問い合わせ（QUERY）で確かめたいとき。問い合わせの副作用は NONE です。",
          "本人を仮名の参照だけで運び、メールアドレス・電話番号の形を基盤へ送りたくないとき。",
          "同意の目的と範囲を、pin した一覧（purpose_refs・scope_refs）に限りたいとき。空の一覧は何も許しません。",
          "基盤から返る receipt をそのまま信じず、request_id・request_digest・record_digest で照合してから記録済みとしたいとき。"
        ],
        "do_not_use_when": [
          "同意が有効かどうかを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
          "記録できたことを同意の権限として使いたいとき。consent_authority は NOT_DERIVED か人・組織に固定され、継手は導きません。",
          "記録した同意を消したり書き換えたりしたいとき。同意の記録は追記だけで、取り消せない外部への書き込み（EXTERNAL_IRREVERSIBLE）です。撤回は RECORD_WITHDRAWAL として記録します。",
          "すぐに使える同意台帳がほしいとき。実の基盤（同意台帳）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
          "個人情報を網羅的に見分けたいとき。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "H-03 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
        ],
        "what_it_checks": [
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "目的と範囲を検めます。pin していない目的（purpose）・範囲（scope）の要求は送りません。",
          "op と副作用の対応を検めます。QUERY は NONE、RECORD_GRANT・RECORD_WITHDRAWAL は EXTERNAL_IRREVERSIBLE で、対応しない要求・receipt は送らず、受理しません。",
          "receipt の状態と record_digest を検めます。op に答えていない状態（RECORD_GRANT なら GRANTED か UNKNOWN、RECORD_WITHDRAWAL なら WITHDRAWN か UNKNOWN 以外）、ほかの同意の record_digest（target が consent_ref と違う）は受理しません。",
          "同意の権限を導いたと名乗る receipt は受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "同意が有効か、同意の権限があるかは判断しません。同意の記録は事実で、権限ではありません。",
          "要る同意・足りない同意・承認経路の事実を扱うのは核（consent-facts。同意を与えません）、保持の事実を扱うのは核（policy-facts）の役目で、法的な権利は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の基盤（同意台帳）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
          "記録できたことは承認ではありません（approval: false）。",
          "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に ConsentRecord の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の purpose_refs・scope_refs。副作用は記録が EXTERNAL_IRREVERSIBLE、QUERY が NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-04",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "データ主体要求（DSAR）処理（運ぶだけ・判断しない） — 監査・プライバシー継手（Tsugite）",
        "summary": "監査・プライバシー継手（Tsugite）は、本人からの請求（DSAR）の要求を検めてから DSAR の窓口の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。DSAR は運ぶだけで、請求に応じるか・何を開示するか・削除するかは判断せず、実行もしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "DSAR を外部の DSAR の窓口へ運ぶとき、本人のメールアドレスや電話番号をそのまま送ってしまうこと、取り消せない効果や不明な効果を運搬の意図にしてしまうこと、そして運べたことを法的な権利（legal entitlement）の判断と取り違えることを防ぎます。",
        "use_when": [
          "DSAR の要求を、本人の仮名の参照だけで DSAR の窓口の基盤へ運びたいとき。",
          "運んだ結果を、request_id・request_digest・参照の一致で照合してから確かめたいとき。DONE の result には transport_state・dsar_type・legal_entitlement が添えられます。",
          "運搬の副作用を NONE〜EXTERNAL_REVERSIBLE の範囲にとどめたいとき。取り消せない効果・不明な効果は意図にできません。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "請求に応じる権利があるか、何を開示してよいかを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
          "開示・削除の請求に応じる窓口そのもの、あるいは開示・削除を実行する仕組みがほしいとき。この継手は DSAR の要求を基盤へ運ぶだけで、削除するかどうかは人・組織が決めます。実の基盤（DSAR の窓口）も含みません。",
          "運べたことを法的な権利の判断として使いたいとき。legal_entitlement は NOT_DERIVED か人・組織に固定され、継手は導きません。",
          "取り消せない効果を DSAR の運搬の意図にしたいとき。その要求は送りません。",
          "個人情報を網羅的に見分けたいとき。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "H-04 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
        ],
        "what_it_checks": [
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "DSAR の意図を検めます。取り消せない効果・不明な効果を意図にした要求は送りません。",
          "法的な権利を判断したと名乗る receipt は受理しません。",
          "receipt の request_id・request_digest・参照が要求と一致しなければ受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "法的な判断をしません。請求に応じる権利があるか、何を開示してよいかは、人・組織と核の役目です。要る同意・足りない同意の事実は核（consent-facts）が扱い、同意を与えません。",
          "開示・削除を決めず、実行もしません。DSAR は運ぶだけで、削除するかどうかは人・組織が決めます。DSAR の運搬は法的な権利の判断ではありません。",
          "実の基盤（DSAR の窓口）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
          "運べたことは承認ではありません（approval: false）。",
          "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に DSAR の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。DONE の result: transport_state・dsar_type・legal_entitlement。利用側が用意するもの: DSAR の窓口の基盤への port（submit、任意で lookup）。副作用は NONE〜EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-05",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "PII 検出・秘匿パイプライン — 監査・プライバシー継手（Tsugite）",
        "summary": "監査・プライバシー継手（Tsugite）は、黒塗り（redaction）の処理の要求（RedactionPipeline）を、pin した黒塗りの定義で検めてから黒塗りの処理の基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。塗る処理そのもの、何を塗るかの決定、塗れているかの確認はしません。外れれば、送らず、受理しません（fail-closed）。",
        "problem": "黒塗りの処理を外部の基盤へ任せるとき、pin していない定義での処理、入力を上書きした結果や外への効果を持つ黒塗りを受け取ること、一部だけ塗れた結果（PARTIAL）を「全部塗れた」と扱うこと、そして黒塗りを開示の許可と取り違えることを防ぎます。",
        "use_when": [
          "黒塗りの処理を、pin した黒塗りの定義（redaction_manifests）に限って基盤へ運びたいとき。",
          "黒塗りの結果を入力と別の参照（output_ref）で受け取り、入力を上書きさせたくないとき。",
          "一部だけ塗れた結果（PARTIAL）を、全部塗れた結果と区別したいとき。PARTIAL では fully_redacted: false です。",
          "黒塗りの副作用を NONE か手元で取り消せるもの（LOCAL_REVERSIBLE）にとどめたいとき。DONE の result には redaction_state・fully_redacted・output_ref・disclosure_authorization が添えられます。"
        ],
        "do_not_use_when": [
          "何を黒塗りにするかを決めさせたいとき。その決定は核（redact-facts）の役目です。",
          "黒塗りが正しく塗れているか（見落とし）を確かめたいとき。この継手は receipt の状態と出力の参照だけを見ます。",
          "機微な情報を網羅的に検出する機能がほしいとき。直接の識別子の検出は、本人の参照に置かれたメールアドレスと電話番号の代表的な形だけを見る網で、すべての個人情報を見分けるものではありません。",
          "黒塗りを開示の許可として使いたいとき。disclosure_authorization は NOT_DERIVED か人・組織に固定され、継手は導きません。",
          "すぐに使える黒塗りの処理の基盤がほしいとき。実の基盤は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "H-05 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
        ],
        "what_it_checks": [
          "黒塗りの定義を検めます。pin していない定義（redaction manifest）の要求は送りません。",
          "入力を上書きした receipt（出力が入力と同じ参照）は受理しません。",
          "外への効果を持つ黒塗りの receipt は受理しません。黒塗りの副作用は NONE か LOCAL_REVERSIBLE で、FAILED は NONE です。",
          "開示の許可を導いたと名乗る receipt は受理しません。",
          "PARTIAL を「全部塗れた」と扱いません（fully_redacted: false）。",
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "塗る処理そのものはしません。黒塗りの処理は基盤の役目で、継手は要求を検めて運び、receipt を照合するだけです。実の基盤（黒塗りの処理）は含まず、port の実装と pin する一覧は接続するときに別の関門で決めます。",
          "何を黒塗りにするかは決めません。核（redact-facts）の役目です。",
          "黒塗りが正しく塗れているか（見落とし）は確かめません。receipt の状態と出力の参照だけを見ます。",
          "個人情報のすべてを見分けるものではありません。直接の識別子の検出は、メールアドレスと電話番号の代表的な形だけを見る網です。",
          "黒塗りは開示の許可ではありません。塗れたことも承認ではありません（approval: false）。",
          "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request) に RedactionPipeline の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の redaction_manifests。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-06",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "保持強制 — 保管ライフサイクル継手（Tsugite）",
        "summary": "保管ライフサイクル継手（Tsugite）は、保持（Retention）の要求（RetentionRequest。EVALUATE・RETAIN・EXPIRE）を、pin した保持方針と論理の target で検めてから保管の backend へ送り、receipt を契約どおりに照合する継手（つぎて）です。取り消せない削除（EXPIRE）は、設定 allow_expire（既定 false）で許し、リーガルホールドの無い対象にだけ送ります（fail-closed）。",
        "problem": "期限での削除・保全の要求を backend へ送るとき、許していない削除やリーガルホールド中の対象の削除を送ってしまうこと、期限切れの判定だけのはずが削除されたと扱うこと、backend の receipt を確かめないまま「消せた」と扱うことを防ぎます。",
        "use_when": [
          "保持方針に沿った保持の要求（RetentionRequest）を、検めてから保管の backend へ運びたいとき。",
          "削除（EXPIRE）の要求を、設定 allow_expire で許したときだけ送りたいとき。",
          "リーガルホールドの事実（核・人の決定）を受け取り、以後その対象の EXPIRE を送らないようにしたいとき。",
          "削除せずに、期限切れかどうかの判定（EVALUATE）だけを backend に求めたいとき。",
          "消せたこと（deleted: true）を、EXPIRE の receipt が EXPIRED と照合できたときだけ事実として扱いたいとき。"
        ],
        "do_not_use_when": [
          "期限が来たら自動で削除・保全する仕組みそのものがほしいとき。期限での削除・保全の実行は基盤の役目で、この継手は渡された保持の要求を運んで照合するだけです。",
          "保持期限やリーガルホールドをこの継手に決めさせたいとき。その決定は核と人の役目で、この継手は事実を受け取って守るだけです。",
          "期限で別の階層へ移したいとき。階層の移動は Archive の要求で、C-09 の面です。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの保管サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定 allow_expire が true でないとき、EXPIRE を送りません。",
          "リーガルホールド中の対象には EXPIRE を送りません。",
          "EVALUATE の receipt が削除を伴っていれば受理しません。EVALUATE の EXPIRED は期限切れの判定だけで、削除ではありません。",
          "RETAIN に EXPIRED を返す receipt は、異常として受理しません。",
          "pin した一覧にない保持方針の要求は送りません。",
          "要求の型を検めます。追加のフィールド、決まった値（const）と違う値、ファイルシステムの target、pin していない backend・許可していない target への要求は送りません。",
          "receipt を照合します。request_digest の偽り、別の target の receipt、外への効果を増やす receipt は受理せず、以後停止します（HALTED）。",
          "FAILED・REFUSED・SIMULATED の receipt は、効果なし（NONE）でなければ受理しません。backend の障害は UNKNOWN とし、同じ request_id を二度送りません。"
        ],
        "what_it_does_not_do": [
          "期限での削除・保全を実行しません。実行は基盤の役目で、この継手は保持の要求を運んで照合するだけです。",
          "保持期限の決定・リーガルホールドの決定はしません。核（redact-facts・policy-facts）と人の役目で、この継手はその事実を受け取って守るだけです。",
          "消せたことは承認ではありません（approval: false）。",
          "実 backend を含みません。どの保管サービスにつなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "H-06 専用の部品ではありません。C-05〜C-09 と F-04 と同じ 1 つの部品が担い、H-06 は C-08 の保持（RetentionRequest）の面です。試験と検査も共通です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/storage-persistence/v1 でつなぎます。createStorageLifecycleClient({ cfg, port }) の submit(request) に RetentionRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返し、DONE に decision・deleted・effect を添えます（どれにも approval: false）。利用側が用意するもの: 保管 backend の port（submit、任意で lookup）と、保持方針・allow_expire の設定。副作用は EXPIRE が EXTERNAL_IRREVERSIBLE、ほかは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-07",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "リーガルホールド（運ぶだけ・判断も凍結もしない） — 監査・プライバシー継手（Tsugite）",
        "summary": "監査・プライバシー継手（Tsugite）は、リーガルホールド（LegalHold）の設定（PLACE）と解除（RELEASE）の要求を、人・組織から受け取った根拠の参照があるときだけ、pin した範囲で検めてリーガルホールドの仕組みの基盤へ運び、receipt を照合する継手（つぎて）です。運ぶだけで、削除の凍結そのものも、ホールドが必要かの判断もしません。外れれば送らず、受理しません（fail-closed）。",
        "problem": "リーガルホールドを外部の仕組みへ運ぶとき、人・組織の根拠が無いまま運んでしまうこと、人・組織以外や ERABAZU 自身を出どころとする根拠で運んでしまうこと、そして運べたことをリーガルホールドの権限と取り違えることを防ぎます。",
        "use_when": [
          "人・組織が出したリーガルホールドの根拠の参照を添えて、設定・解除の要求を基盤へ運びたいとき。根拠は recordAuthorityEvidence(evidence_ref, origin) で受け取ります（origin は HUMAN か ORGANIZATION だけ）。",
          "リーガルホールドの範囲を、pin した一覧（scope_refs）に限りたいとき。",
          "receipt の状態が op に答えている（PLACE→PLACED、RELEASE→RELEASED）か FAILED であることを照合してから、運べたとしたいとき。DONE の result には hold_state・legal_hold_authority が添えられます。",
          "基盤の障害で結果が不明になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "リーガルホールドが必要かを判断させたいとき。法的な判断はせず、人・組織と核の役目です。",
          "根拠が本物の決定かを確かめたいとき。根拠の中身は検めず、人・組織から受け取った参照かどうかだけを見ます。",
          "運べたことをリーガルホールドの権限として使いたいとき。legal_hold_authority は NOT_DERIVED か人・組織に固定され、継手は導きません。",
          "削除の凍結を行う仕組みそのものがほしいとき。この継手は要求を運ぶだけで、実の基盤（リーガルホールドの仕組み）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
          "人・組織の根拠がまだ無いとき。根拠の無い要求は送りません（REFUSED）。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "H-07 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
        ],
        "what_it_checks": [
          "根拠の有無を検めます。人・組織の根拠が無い要求は送りません（REFUSED）。",
          "根拠の出どころを検めます。人・組織（HUMAN・ORGANIZATION）以外からの根拠、ERABAZU を出どころとする根拠では運びません。",
          "範囲を検めます。pin していない範囲の要求は送りません。",
          "リーガルホールドの権限を名乗る receipt は受理しません。",
          "receipt の状態を検めます。op に答えていない状態（PLACE→PLACED、RELEASE→RELEASED、FAILED のどれでもないもの）は受理しません。",
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "リーガルホールドが必要かを判断しません。法的な判断は人・組織と核の役目です。",
          "リーガルホールドの根拠を作りません（ERABAZU は根拠を作りません）。根拠の中身（本物の決定か）も検めず、人・組織から受け取った参照かどうかだけを見ます。",
          "削除の凍結（保全）を自分では行いません。要求を基盤へ運び、receipt を照合するだけです。実の基盤は含みません。",
          "保持・リーガルホールドの方針の事実を扱うのは核（policy-facts）、権限は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "運べたことはリーガルホールドの権限ではなく、承認でもありません（approval: false）。",
          "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の recordAuthorityEvidence(evidence_ref, origin) で根拠を受け取った後、submit(request) に LegalHold の Request を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg の scope_refs。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-08",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "改ざん検知チェーン — 監査台帳継手（Tsugite）",
        "summary": "監査台帳継手（Tsugite）は、監査イベント封筒（AuditEventEnvelope）を前のハッシュ（previous_hash）でつなぐ改ざん検知（tamper-evident）の連鎖について、連鎖と digest を契約の式どおりに検めてから追記専用（append-only）の backend へ送り、返る受領（AuditAppendReceipt）の連鎖のハッシュを照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "ハッシュでつないだ監査の連鎖は、どこかで前のハッシュが書き換えられたり、digest の計算の仕方が食い違ったりすると、改ざんを見つける力を失います。この継手は、送る前の封筒と保管先の受領の両方を同じ契約の式で検め、式と合わない連鎖を送らず、受理しないことで、照合できない連鎖を信頼しないようにします。",
        "use_when": [
          "監査イベントを、前のハッシュでつないだ改ざん検知の連鎖として外部の追記専用 backend に残したいとき。",
          "連鎖のハッシュを、決まった式（RFC 8785 に制限した正準化と sha256）でそろえて計算・照合したいとき。",
          "保管先が返す受領の event_digest や連鎖のハッシュをそのまま信じず、送った封筒から計算し直して照合してから記録済みとしたいとき。",
          "連鎖の食い違いが見つかったら、人が確かめるまで追記を止めたいとき。",
          "封筒の検めと受領の照合だけを、backend を呼ばずに試したいとき。純粋な関数 checkEnvelope と checkReceipt が { ok, errors } を返します。"
        ],
        "do_not_use_when": [
          "透明性ログ（Merkle の包含証明）が要るとき。この継手が扱うのは前のハッシュでつなぐ連鎖で、包含証明は範囲外です。",
          "改ざんがあったかどうかの最終の判断を、この継手に任せたいとき。改変不能かの決定は核（provenance-facts・integrity-facts）の役目です。",
          "RFC 8785 に制限した正準化と sha256 ではない、別のハッシュの式を使いたいとき。式の違うハッシュは受理しません。",
          "監査イベントの本文そのものを連鎖に載せたいとき。封筒には契約のフィールドだけを載せ、本文の混入は拒否します。",
          "すぐに使える保管先がほしいとき。実 backend は含まず、どの追記専用サービスにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "前のハッシュ（previous_hash）を、台帳の先頭（head）と照らして検めます。previous_hash が書き換えられた封筒は、送らず、受理しません。",
          "連鎖のハッシュを契約の式で計算し直します。RFC 8785 に制限した正準化と sha256 で求め、正準化していない形で計算したハッシュは受理しません。",
          "連鎖の始まり（genesis）の規則を検めます。genesis の previous_hash_value は空文字として計算し、genesis の規則に反する封筒は送りません。",
          "digest を検めます。digest の対象（domain）を取り違えて計算したものは受理しません。",
          "受領（AuditAppendReceipt）を照合します。event_digest の偽り、連鎖のハッシュの誤り、台帳・通番・event_id・対象の食い違いがあれば、受理しません（RECEIPT_REJECTED）。",
          "受領に不整合があれば停止します（HALTED）。人が確かめるまで追記しません。",
          "通番（sequence）の巻き戻し・飛びがあれば、送らず、受理しません。連鎖の順番が崩れた追記を通しません。"
        ],
        "what_it_does_not_do": [
          "改変不能かどうか、改ざんがあったかどうかを決めるのは核（provenance-facts・integrity-facts）の役目です。この継手がするのは、送る前の検めと受領の照合だけです。",
          "透明性ログ（Merkle の包含証明）は扱いません。",
          "実 backend を含みません。どの追記専用サービスにつなぐか、port の実装、pin する対象（endpoint・受領の形式）は未定で、接続するときに別の関門で決めます。",
          "backend の認証に使う秘密（SecretRef）の解決はしません。port 側の責務です。",
          "追記の成功は承認ではありません（approval: false）。",
          "契約とハッシュの式を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "H-08 専用の部品ではありません。H-01（改変不能監査ログ）・C-04（追記専用台帳）・H-02（アクセスログ）と同じ 1 つの部品が担い、試験と検査も共通です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-ledger/v1 でつなぎます。createLedgerClient({ cfg, port }) の append(envelope) に AuditEventEnvelope を渡すと APPENDED_VERIFIED・ALREADY_APPENDED・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。検めだけなら checkEnvelope と checkReceipt が { ok, errors } を返します。利用側は追記専用 backend への port（append、任意で lookup）を用意します。副作用は EXTERNAL_IRREVERSIBLE（取り消せない書き込み）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-09",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "規制レポート出力（運ぶだけ・当局の受理ではない） — 監査・プライバシー継手（Tsugite）",
        "summary": "監査・プライバシー継手（Tsugite）は、規制報告（RegulatoryReport）の要求と報告の中身（bytes）を、pin した報告の形式・提出先と、digest・上限で検めてから規制当局の窓口の基盤へ運び、receipt を照合する継手（つぎて）です。運ぶだけで、報告の中身は利用側が渡し、届けたことは規制当局の受理ではありません。外れれば送らず、受理しません（fail-closed）。",
        "problem": "規制報告を外部の提出窓口へ運ぶとき、pin していない形式や提出先へ送ること、書き換わった中身や大きすぎる中身を送ること、許していない取り消せない提出をしてしまうこと、そして届けたことを規制当局の受理と取り違えることを防ぎます。",
        "use_when": [
          "規制報告の形式と提出先を、pin した一覧（report_profiles・report_destinations）に限りたいとき。",
          "報告の中身（bytes）の digest と上限（max_report_bytes）を確かめてから運びたいとき。max_report_bytes は報告を使うとき必須です。",
          "取り消せない提出を、設定 allow_irreversible: true のときだけに限りたいとき（既定は false）。",
          "生成（GENERATED）と届けた（DELIVERED）を副作用と照らして区別したいとき。GENERATED は NONE か LOCAL_REVERSIBLE、DELIVERED は EXTERNAL_* です。DONE の result には delivery_state・delivered・regulatory_acceptance が添えられます。"
        ],
        "do_not_use_when": [
          "規制当局が受理したかを知りたいとき。届けた報告は規制当局の受理ではなく、regulatory_acceptance は NOT_DERIVED か人・組織に固定され、継手は導きません。",
          "届いた先が pin した提出先かを receipt で確かめたいとき。規制報告の receipt には提出先の欄が無く、確かめられるのは送る前の pin だけです。",
          "報告の中身を作らせたいとき。中身は利用側が submit(request, bytes) の bytes として渡し、継手が確かめるのは digest と上限です。",
          "法的な判断をさせたいとき。この継手は法的な判断をせず、人・組織と核の役目です。",
          "すぐに使える提出の窓口がほしいとき。実の基盤（規制当局の窓口）は含まず、port の実装と pin する一覧は接続するときに別の関門（gate）で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "H-09 だけの部品を求めるとき、またはほかの H の項目を担わせたいとき。この継手は H-03・H-04・H-05・H-07・H-09 を契約上 1 つの部品として担い、試験と検査も共通です。H-01・H-08 は監査台帳継手、H-06 は保管ライフサイクル継手（C-08）、H-02 は監査台帳か observability の sink が担います。"
        ],
        "what_it_checks": [
          "報告の形式と提出先を検めます。pin していない形式（report profile）・提出先（report destination）の要求は送りません。",
          "報告の中身を検めます。bytes の digest が合わないもの、上限を超えるものは送りません。",
          "取り消せない提出を検めます。allow_irreversible: true でなければ送りません。",
          "状態と副作用の対応を検めます。GENERATED なのに外への効果を名乗る receipt、DELIVERED なのに効果の無い receipt は受理しません。",
          "規制当局の受理を導いたと名乗る receipt は受理しません。",
          "本人の参照と参照の形を検めます。メールアドレス・電話番号の形の本人の参照、空の参照、パスや .. を含む参照（path traversal）、追加のフィールドは送りません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、request_digest を偽った receipt は受理しません。受理できない receipt が来たら停止します（HALTED）。",
          "同じ request_id は二度送りません。基盤の障害は UNKNOWN とし、再送は lookup で解きます。"
        ],
        "what_it_does_not_do": [
          "規制当局の受理を導きません。届けたことは受理ではなく、承認でもありません（approval: false）。",
          "届いた先を receipt で確かめません。規制報告の receipt には提出先の欄が無く、送る前の pin だけで検めます。",
          "報告の中身について継手が確かめるのは digest と上限だけです。法的な判断はしません（人・組織と核の役目）。",
          "法的な権利・開示の許可は人・組織の役目です。この継手がするのは、送る前と受け取った後の検めだけです。",
          "実の基盤（規制当局の窓口）を含みません。port の実装と pin する一覧は未定で、接続するときに別の関門で決めます。",
          "プライバシー法令への適合を約束するものではありません。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/audit-privacy/v1 でつなぎます。createAuditPrivacyClient({ cfg, port }) の submit(request, bytes) に RegulatoryReport の Request と中身の bytes を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 基盤への port（submit、任意で lookup）と cfg（pin する形式・提出先、max_report_bytes、allow_irreversible）。副作用は NONE〜EXTERNAL_*。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-10",
        "category": "H/監査・統制・プライバシー",
        "kind": "Primitive",
        "title": "系譜（lineage）の記録 — 系譜（lineage）の記録の部品（Primitive）",
        "summary": "系譜（lineage）の記録の部品（Primitive）は、集計に使った入力の集合（Ref の一覧）・集計器の参照と版・出力の bytes から系譜の記録（erabazu.lineage/1）を作り、あとで入力と出力から計算し直して照合する単独の部品です。入力の順は結果を変えず、重複は黙って除かずに断ります。照合の違いはエラーではなく、違った欄の一覧で返します。集計器は動かしません。",
        "problem": "数字の出どころを後で問われたときの裏付けになります。どの入力を使い、どの集計器の版で、どの出力ができたかを、入力の集合のハッシュ・集計器の版・出力のハッシュとして残します。重複した入力を黙って除かないので、どの入力を使ったかの数え違いを隠しません。",
        "use_when": [
          "集計の入力の集合・集計器の参照と版・出力の bytes・記録の時刻から、系譜の記録を作りたいとき（recordLineage）。",
          "入力の集合を、並べ順に左右されない digest にしたいとき。inputs を UTF-16 の符号単位の順に並べた配列の、RFC 8785 の正準 JSON の sha256 です。",
          "あとで入力と出力から計算し直し、記録と同じかを確かめたいとき（checkLineage。違いは matches: false と mismatched の一覧で、エラーではない）。",
          "どの欄が違ったか（input_set.count・input_set.digest・output.digest・output.size）を知りたいとき。",
          "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。recorded_at は呼ぶ側が渡します。"
        ],
        "do_not_use_when": [
          "集計器が正しいこと・出力が正しいことの証明が要るとき。この部品は集計器を動かさず、系譜は使ったものの記録です。",
          "本当にその版で動かしたか・その時刻かを確かめたいとき。tool の ref・version と recorded_at は渡された値をそのまま記録し、照合では形だけを検めて値は比べません。",
          "inputs の Ref が実在する入力を指すか、その中身を確かめたいとき。Ref の形だけを検め、中身が変わっても Ref が同じなら digest は同じです。",
          "記録そのものの改ざんを止めたいとき。それは署名（B-12）や封（H-11）の側の役目です。",
          "時差の付いた時刻（+09:00 など）やうるう秒で記録したいとき。recorded_at は UTC の Z だけで、ほかは INVALID_TIME で止めます。",
          "入力を 1,048,576 個より多く扱うとき。INVALID_INPUTS で止めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "inputs に同じ文字列が 2 回以上あるとき、黙って除かずに DUPLICATE_INPUT で止めます（文字列の完全一致で比べ、大文字と小文字は別の入力）。",
          "inputs が素の配列でない、穴・余分なプロパティ・getter の要素を持つ、Ref でない要素を持つ、1,048,576 個を超えるとき、INVALID_INPUTS で止めます。",
          "tool_ref が Ref でない、tool_version が 1〜200 文字でない・制御文字や対になっていないサロゲートを含むとき INVALID_TOOL、output_bytes が Uint8Array でないとき NOT_BYTES で止めます。",
          "recorded_at が RFC 3339 の UTC（Z）の形でない、または実在しない日時（2 月 30 日・24 時・60 秒など）のとき、INVALID_TIME で止めます。",
          "checkLineage の record の形が違うとき（欄の過不足・schema の違い・負の count・小文字 64 桁の hex でない digest など）、INVALID_RECORD と最初に見つけた欄の名前（field）で止めます。",
          "引数が素のオブジェクトでない・決まった欄のほかを持つとき INVALID_ARGUMENT、引数を読むこと自体が例外になるとき INSPECTION_FAILED で止めます。欄はそれぞれ一度だけ読みます。"
        ],
        "what_it_does_not_do": [
          "系譜は「何を使って何ができたか」の記録で、集計器が正しいこと・出力が正しいこと・記録が改ざんされていないことの証明ではありません。matches: true は、渡した入力と出力が記録と同じだったことだけを示します。",
          "記録を保管しません。状態・時計・外の接続を持ちません。",
          "inputs が 0 個でも記録を作ります（count 0、digest は \"[]\" の sha256）。0 個を断ることはしません。",
          "SHA-256 の計算そのものは Node.js の crypto に任せていて、ファズのオラクルも同じ crypto を使います。",
          "正準化は I-08 の部品のコードの写し（src/canonical.js）を使います。写しの中の分岐そのものは、この部品の試験では通りません（写しの試験は I-08 の側）。",
          "記録・照合の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の来歴（provenance）の仕様の実装ではありません。"
        ],
        "integration": "src/lineage.js の recordLineage({ inputs, tool_ref, tool_version, output_bytes, recorded_at }) と checkLineage({ record, inputs, output_bytes }) を呼びます（投げません）。record は schema（erabazu.lineage/1）・input_set（count・digest）・tool（ref・version）・output（digest・size）・recorded_at です。返すのは record か matches・mismatched で、止めるときは { ok: false, errors: [{ code }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-11",
        "category": "H/監査・統制・プライバシー",
        "kind": "Component",
        "title": "封印（追記式の署名・日次の根） — 封印（追記式の署名・日次の根）の部品（Component）",
        "summary": "封印の部品（Component）は、Merkle 木（部品(Primitive) B-11）・手元の鍵での署名（部品(Primitive) B-12）・冪等書込の継手（C-05）・任意の固定の port（H-01 の形）を束ねる部品です。appendSeal は、呼ぶ側が渡した前の封印の記録の鎖を検め、足したファイルを含む新しい根に署名した記録を、次の seq の別の物として書きます。finalizeDay は区画ごとの鎖を検めて日の根を作り、seal/roots/<date>.json と署名した日の記録を書き、固定の port があれば固定します。どの結果も承認ではなく、時刻の権威でもありません。",
        "problem": "生データが後から書き換えられていないことの裏付けのための部品です。署名し直さない書き換えや信頼しない鍵の記録に続けて書くこと、証明や呼ぶ側の言う木の大きさを信じること、記録を上書きすること、固定できていないのに固定したと言うことを防ぎます。",
        "use_when": [
          "区画（folder_ref）に足したファイル（name と sha256、1〜10000 個）ごとに、前の記録の鎖に続く署名した封印の記録を <folder_ref>/seal/<seq>.json に 1 回だけ書きたいとき（appendSeal）。",
          "日ごとに、区画の根を folder_ref の順に束ねた日の根を作り、canonical({ seal_date, root }) の形のファイルと、署名した日の記録を書きたいとき（finalizeDay）。日の記録は時計を含まないので、同じ鎖からは同じ bytes になります。",
          "日の根を、注入した固定の port（H-01 の append と同じ形）で外へ固定し、その receipt を seal/roots/<date>.anchor.json に残したいとき。固定の port は根のファイルと日の記録が書けた後にだけ呼びます。",
          "署名した 1 件の記録に対して、ファイルの包含の証明を検めたいとき（verifyFile）。証明を鎖から作りたいとき（proveFile）。",
          "署名を、秘密鍵を閉じ込めた sign の port に任せ、この部品に秘密鍵を渡したくないとき。署名するのは keys の最初の鍵だけで、ほかの鍵は前の記録の検めにだけ使います。",
          "結果の分からなかった書込・固定を、同じ要求・同じ bytes・同じ envelope で送り直したいとき（resolvePending）。"
        ],
        "do_not_use_when": [
          "固定の port を付けずに、「この日時に存在した」ことの裏付けとして使いたいとき。固定の port が無ければ anchored: false で、記録は「これが署名した内容」だけです。sealed_at は注入した時計の値で、時刻の権威ではありません（信頼できる時刻は B-06）。",
          "署名を、ファイルの中身や取得の正しさ・業務の判断・承認として使いたいとき。署名は「keys の鍵の持ち主がこの bytes に署名した」ことだけです。",
          "鍵の置き場所・保管・入れ替え・失効まで任せたいとき。この部品の外で、どの鍵をいつまで信じるかは決めていません。",
          "verifyFile だけで鎖まで確かめたいとき。verifyFile は記録 1 件の署名と包含だけを検め、信頼する鍵で誤った tree_size に署名された記録は見分けられません。鎖の検めは appendSeal・finalizeDay・proveFile で行います。",
          "この部品に保管を読ませたいとき。この部品は保管を読みません。鎖は呼ぶ側が保管から読んで渡します。保管に無い版の記録を渡されると、それに続く記録を書き、以後その区画は SEAL_CHAIN_BROKEN で止まります（直すのは人）。",
          "固定の port 付きの finalizeDay のやり直しを、成功として受け取りたいとき。固定の記録は anchor_status を含むので中身が変わり、保管が断って WRITE_REFUSED で終わります。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・keys が 1〜16 個でない・key_id や指紋が重なる・port の関数が無いとき INVALID_OPTIONS、操作の入力の形が違うとき INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "渡した鎖が検めを通らないとき（正準化・欄・schema・folder_ref・seq・名前の重なり・tree_size・prev_entry_sha256・信頼する鍵の署名・前の根との一貫性・ファイルから計算し直した根）、SEAL_CHAIN_BROKEN で何も書きません。errors[0] に folder_ref・at・reason を添えます。",
          "tree_size と根は、署名した記録とファイルの数から取り直します。一貫性・包含の証明や呼ぶ側の言う大きさは使いません（RFC 9162 の証明は木の大きさを縛りません）。",
          "足すファイルの名前が重なる・鎖に既にあるとき DUPLICATE_NAME、finalizeDay で同じ folder_ref が 2 回あるとき DUPLICATE_FOLDER、区画の記録が 10000 件かファイルが 100000 個を超えるとき SEAL_LIMIT_EXCEEDED で止めます。",
          "sign の port が投げた・ok でないとき SIGN_FAILED、結果が Ed25519・最初の鍵の key_id と指紋でない・最初の鍵の公開鍵での往復の検めが通らないとき SIGNER_MISMATCH、B-11・B-12 の port の故障や形の違いのとき MERKLE_PORT_FAILED・VERIFY_PORT_FAILED で、書きません。",
          "書込の結果が分からないとき WRITE_UNKNOWN、送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。固定の port の結果が分からないとき ANCHOR_UNKNOWN、断ったとき ANCHOR_REFUSED、停止・receipt の照合の失敗・形の違いのとき ANCHOR_FAILED で、anchored: true にしません。保留の間の appendSeal・finalizeDay は SEAL_PENDING です。",
          "verifyFile で、記録が正準化できない・形が違う・信頼する鍵の署名でないとき ENTRY_UNVERIFIED、証明の tree_size が記録と違う・leaf_index が大きすぎるとき PROOF_NOT_FOR_ENTRY、proveFile で名前が鎖に無いとき NAME_NOT_SEALED です。",
          "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
        ],
        "what_it_does_not_do": [
          "封印の部品は継手ではありません。自分の Request/Receipt は無く、書込は束ねた C-05 の継手が自分の契約で、固定は注入した port がその契約で起こします（台帳への追記・Git の push は取り消せない効果です）。この部品自身は外へ触れません。",
          "束ねた部品の意味（根が正しい ≠ 中身が正しい・署名 ≠ 承認・固定した ≠ 権威ある時刻で刻んだ）は、それぞれの部品の側に残ります。",
          "どの結果も、承認でも権限でも時刻の権威でもありません（approval: false・authority: NOT_DERIVED・timestamp_authority: NOT_DERIVED、derives_authority: false）。",
          "外への固定の強さは、注入した固定の port とその先の強さまでです。この部品は port が固定したと言ったことと receipt を記録するだけで、固定先を自分では確かめません。",
          "上書き・削除の要求は送りません。秘密鍵を見ません（sign の port の中だけ）。",
          "単体試験とファズは代役です（ファズの署名の代役は Ed25519 ではなく、速さのための SHA-512 の代役）。本物の B-11・B-12・C-05・H-01 との結線は、別の結線の検めで確かめました。Git の固定は、成功する経路だけを結線で確かめ、断り・衝突・non-fast-forward の経路は試していません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の透明性ログや署名の製品の雛形ではありません。"
        ],
        "integration": "src/seal.js の createSeal(options) で作り、{ ok, errors, instance } を返します。options は target・request_prefix・keys・ports・任意の timestamp です。操作は appendSeal・finalizeDay・verifyFile・proveFile・resolvePending・status で、結果に approval: false が付きます。port は merkle（B-11 の 5 つの関数）・sign（秘密鍵を閉じ込めた closure）・verify と publicKeyFingerprint（B-12）・write.submit（C-05 の継手のクライアント）・任意の anchor.append です。資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "H-12",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "封印の根の Git への固定 — 封印の根の Git への固定の継手（Tsugite）",
        "summary": "封印の根の Git への固定の継手（Tsugite）は、日次の封印の根のファイル（seal/roots/YYYY-MM-DD.json）を、Git の決まった枝へ前の版（parent_commit）の子として commit し、fast-forward で push する要求（SealAnchorRequest）を検めてから port へ運び、返った receipt（SealAnchorReceipt）を照合する継手（つぎて）です。追記だけで、外れたら送らず、受理しません（fail-closed）。",
        "problem": "「いつの時点の根か」を外から照らせるようにします。そのとき、根を上書きしてしまうこと、同じ日付に別の根を固定してしまうこと、push の結果が見えないまま二重に commit してしまうこと、commit の日時を時刻の権威と取り違えることを防ぎます。",
        "use_when": [
          "封印の部品が書いた日次の根のファイルを、Git のリポジトリへ追記だけで固定したいとき。",
          "送る前に、要求の root_digest からファイルの中身 canonical({seal_date, root}) を作り直し、その sha256 が file_digest と同じことを確かめたいとき。",
          "1 つの日付に 1 つの根を守りたいとき。食い違い（CONFLICT）の日付は、人の判断まで送りません。",
          "push の結果が見えないとき、相手の側（branch の先頭と path のファイル）を読んで解いてから送り直したいとき（port.inspect と SealInspectRequest / SealInspectReceipt）。"
        ],
        "do_not_use_when": [
          "根そのもの（Merkle の計算・署名）を作りたい・確かめたいとき。それは封印の部品の役目で、この継手は根の digest からファイルの digest を作り直して照らすだけです。",
          "信頼できる時刻で刻みたいとき。commit の日時もホストの push の時刻も観測です。信頼できる時刻は B-06（RFC 3161）の継手で別に刻みます。",
          "履歴の書き換え（上書き・force-push・amend・削除）をしたいとき。要求にも設定にもその欄はありません。",
          "特定の Git ホスティング（GitHub など）の API や git コマンドの使い方の雛形がほしいとき。この継手はその雛形ではありません。",
          "すぐに使える固定先がほしいとき。実の Git・remote への commit・push は 0 回で、port の実装・資格情報・ブランチ保護の設定は、接続するときに別の関門で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定と要求を検めます。repository・branch・committer は設定の一覧（repositories・branches・committer_refs）の中だけです。branch_ref は git check-ref-format に合うものだけで、暦に無い seal_date は送りません。空の一覧は何も許しません。",
          "ファイルの digest を照らします。作り直した sha256 と file_digest が違う要求は送りません。committed_file_digest が違う receipt、ANCHORED の parent_commit が依頼と違う receipt、changed_paths がちょうど [path] でない receipt は受理しません。",
          "追記だけを守ります。history_rewrite が NOT_PERFORMED でない receipt は受理しません。設定に知らないキー（force_push など）があれば、何も送りません。",
          "日付の状態を照合します。知っている日付に別の根は送りません。知っている日付への 2 度目の ANCHORED、知っている根と違う ALREADY_ANCHORED は受理せず、停止します（HALTED）。",
          "commit の桁を照合します。commit_id は依頼の parent_commit と同じ桁（40 桁か 64 桁、リポジトリの形式）のときだけ受け取ります。",
          "効果を増やす receipt、FAILED のほかで効果の不明な receipt、状態の表の外の receipt、別の要求の receipt は受理せず、停止します。ANCHORED の効果は EXTERNAL_IRREVERSIBLE、ほかは NONE（FAILED は NONE か UNKNOWN）です。",
          "結果の見えない push（port の障害、FAILED・UNKNOWN の receipt）は UNKNOWN（未解決）とし、成功にも失敗の確定にもしません。相手の側を読めないときは送り直さず、解けるまでほかの要求も送りません。",
          "push の資格情報は参照（committer_ref）だけです。committer_refs に封印の署名の鍵の参照（seal_signing_key_refs）が入っている設定は、誤りとして拒否します。"
        ],
        "what_it_does_not_do": [
          "commit の中身が本当にその親とその変更か、push が fast-forward だったか、読んだ答えが本当の Git の状態かは確かめません（port の申告。この継手は git のオブジェクトを読みません）。",
          "日付の状態はこの継手の中だけで、再起動で消えます。この継手が知らない日付の上書きは見分けられません。上書き・force-push の禁止は、repository のブランチ保護で別に守ります。",
          "commit の日時・host_time の正しさも順序も確かめません。どちらも時刻の権威ではありません（commit_date_authority・timestamp_authority は NOT_DERIVED）。",
          "根そのものの正しさは確かめません。CONFLICT の解決は人の判断です。",
          "固定できたことは承認ではなく、権威ある時刻で刻んだことでも、根が正しいことでもありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/seal-anchor-git/v1（SealAnchorRequest / SealAnchorReceipt、読む口 SealInspectRequest / SealInspectReceipt）でつなぎます。createSealAnchorClient({ cfg, port, clock }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Git の port（submit、任意の lookup・inspect）と cfg の一覧。副作用: ANCHORED だけ EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-01",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "入力スキーマ検証 — 入力スキーマ検証の部品（Primitive）",
        "summary": "入力スキーマ検証の部品（Primitive）は、JSON の値を、JSON Schema 2020-12 の全体ではなく決まった一部（profile json-schema-subset/v1）で検める単独の部品です。使えるキーワードは決まった 28 個だけで、ほかのキーワード（format を含む）はスキーマごと断ります（黙って読み飛ばしません）。結果は { ok, errors } で、止めるときは最初の 1 件のコードと位置（JSON Pointer）を返します（fail-closed）。",
        "problem": "入力の形の妥当性を検めるときに、知らないキーワード・外の $ref・壊れた値を黙って読み飛ばして「通った」ことにしてしまうことを防ぎます。使えるキーワードを固定し、一部の外のものはスキーマごと断ります。",
        "use_when": [
          "JSON の値の形（type・required・properties・additionalProperties・長さ・範囲・pattern・enum・const など）を、決まった一部のキーワードで検めたいとき（validate）。",
          "スキーマそのものが一部の中に収まっているかを、インスタンスなしで先に確かめたいとき（checkSchema）。",
          "止めた理由を、コード・instance_path・schema_path（JSON Pointer）の最初の 1 件として、決まった順で受け取りたいとき。",
          "getter を呼ばず、スキーマとインスタンスを 1 回だけ読んだ写しで検めたいとき（途中で中身が変わらない）。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。validate と checkSchema は例外を投げません。"
        ],
        "do_not_use_when": [
          "JSON Schema 2020-12 の全体が要るとき。使えるのは決まった 28 のキーワードだけで、format は検めずに断ります。",
          "いまの実物の契約のスキーマ（00a の 17 本）をそのまま検めたいとき。17 本はどれも一部の外（format・runtime_validator_contract・uniqueItems・hash_computation・外の $ref）を使うので、スキーマごと断られます。",
          "外の文書・$id による基底の URI・$anchor・$dynamicRef を使う $ref が要るとき。$ref は \"#\" と \"#/$defs/<名前>\" だけです。",
          "信頼できないスキーマの pattern をそのまま使うとき。正規表現の爆発（ReDoS）は止めず、評価の回数の上限は正規表現の中の時間を数えません。",
          "文字列・配列の長さや const・enum の比較の大きさに上限が要るとき。上限は持たず、I-06 の部品か使う側で決めます。",
          "2^53 以上の整数を integer として扱いたいときや、見た目が同じ文字列を同じとして扱いたいとき。integer は安全な整数だけで、Unicode の正規化はしません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED。RegExp の u フラグの Unicode の版はエンジンに依ります）。"
        ],
        "what_it_checks": [
          "一部の外のキーワード（format を含む）が、どこにあっても（使われない $defs の中でも）あるとき、根でない $schema・$id・$defs があるとき、スキーマごと UNSUPPORTED_KEYWORD で止めます。",
          "$schema が 2020-12 でないとき UNSUPPORTED_DIALECT、$ref の形が違うとき（外の文書・アンカー・% ・~ の書き方）UNSUPPORTED_REF、名前が $defs に無いとき UNRESOLVED_REF で止めます。",
          "インスタンスを進めずに回る $ref があるとき、たどり着くかどうかに関わらず REF_CYCLE で止めます。",
          "キーワードの値の形が違うとき（空の enum・重なる required や type・負の数・配列の形の items・if の無い then）INVALID_SCHEMA、pattern が作れないとき INVALID_PATTERN で止めます。",
          "スキーマが JSON の値でないとき（getter を含む）SCHEMA_NOT_JSON、インスタンスが JSON の値でないとき（undefined・関数・NaN・bigint・Date・穴のある配列・symbol のキー・getter など）INSTANCE_NOT_JSON で止めます。",
          "入れ子が 256 段を超えるとき SCHEMA_TOO_DEEP・INSTANCE_TOO_DEEP、評価の深さが 512 に届くとき EVALUATION_TOO_DEEP、評価の回数が 1,000,000 を超えるとき EVALUATION_LIMIT で止めます。not・anyOf・oneOf・if の中で届いても「通らない」に化けません。",
          "インスタンスがスキーマに合わないとき、TYPE_MISMATCH・REQUIRED_MISSING・ADDITIONAL_PROPERTY・PATTERN_MISMATCH・ONE_OF_MANY_MATCHED など、キーワードごとの決まったコードで止めます。2^53 以上の整数は integer に当てはまりません。",
          "値を調べること自体が例外になるとき（Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。Proxy が答えを変えても、1 回だけ読んだ写しを検めるので途中で結果が変わりません。"
        ],
        "what_it_does_not_do": [
          "JSON Schema の全体ではなく一部だけを扱います（決まった 28 のキーワード）。format は検めずに断り、外の文書・$anchor・$dynamicRef の $ref も断ります。",
          "実物の契約のスキーマ（00a の 17 本）は、どれも一部の外として断られます。一部は黙って広げておらず、一部を広げるか、契約の側を一部に合わせるかは別の決定です。",
          "公式の JSON-Schema-Test-Suite にはかけていません。既知の答えは、この部品の試験に書いたものです。",
          "pattern の正規表現の爆発（ReDoS）は止めません。信頼できないスキーマの pattern は使う側で制限します。",
          "スキーマに合うことは形の検めで、値の中身が正しいことでも、受け入れてよいことでも、許可でもありません（意味の判断は Kernel、承認は人。result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の JSON Schema のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/schema-validate.js の validate(schema, instance) を呼びます（投げません）。checkSchema(schema) はスキーマだけを検め（投げません）、assertValid は合わなければ SchemaValidateError を投げます。受け取るのは、決まったキーワードだけのスキーマと、JSON の値のインスタンスです。返すのは { ok: true, errors: [] } か { ok: false, errors: [{ code, instance_path, schema_path }] }（最初の 1 件。code は error_taxonomy）。外の $ref は読みに行きません。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-02",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "出力エンコード — 出力エンコードの部品（Primitive）",
        "summary": "出力エンコードの部品（Primitive）は、文字列を、決めた 1 つの出力先（context）の中で文字として読まれる形にする単独の部品です。context は html_text・html_attribute_quoted・url_component・json_in_html_script の 4 つだけです。決めていない context・文字列でない値・単独のサロゲート・長すぎる入力は、決まったコードで止めます（fail-closed）。HTML の無害化（sanitize）ではありません。",
        "problem": "文字列を HTML・URL・<script> の中に書き出すときに、context を決めずに推測で符号化してしまうことや、壊れた文字列（単独のサロゲート）を黙って置き換えてしまうことを防ぎます。context は名前で明示させ、既定の context はありません。",
        "use_when": [
          "文字列を HTML の要素の中身（テキスト）に入れたいとき（html_text。& < > \" ' を実体参照にします）。",
          "文字列を二重引用符で囲んだ HTML の属性の値に入れたいとき（html_attribute_quoted。html_text の 5 つに加えて ` も符号化します）。",
          "文字列を URL の 1 つの部分（パスの 1 区切り・クエリの名前や値）に入れたいとき（url_component。RFC 3986 の unreserved のほかを %XX にします）。",
          "文字列を HTML の <script> 要素の中に、JSON の文字列リテラルとして置きたいとき（json_in_html_script。< > & U+2028 U+2029 を \\u の形にし、</script> や <!-- で要素が閉じられません）。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。encode は例外を投げず、逆向きに戻すと元の text になります。"
        ],
        "do_not_use_when": [
          "タグや属性を残したい HTML の入力を無害化したいとき。この部品は HTML の無害化（sanitize）ではなく、すべてを文字として書きます。",
          "引用符で囲まない属性・イベント属性（onclick など）・style 属性・URL を取る属性（href・src の javascript: など）の値の意味を防ぎたいとき。html_attribute_quoted はそれを防ぎません。",
          "URL 全体の検め（スキームの許可・ホストの検め）が要るとき。url_component は URL の 1 つの部分のためです。",
          "HTML の属性の中・JavaScript の文字列やテンプレートリテラルの中・style などの生テキストの要素に書くとき。json_in_html_script は <script> 要素の中のためです。",
          "CSS・JavaScript の識別子・XML・SQL・シェル・ヘッダーなどの context に書くとき。これらは扱いません。",
          "1,048,576 UTF-16 符号単位より長い文字列を扱うとき。INPUT_TOO_LARGE で止めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "context が決めた 4 つのどれでもないとき（大文字や空白の違い・Object.prototype の名前・文字列でない値を含む）、UNKNOWN_CONTEXT で止めます。",
          "text がプリミティブの文字列でないとき（数・null・undefined・String オブジェクト・配列・Symbol・bigint・Proxy）、NOT_A_STRING で止めます。",
          "text が 1,048,576 UTF-16 符号単位より長いとき、INPUT_TOO_LARGE で止めます。",
          "text に単独のサロゲート（対になっていない U+D800〜U+DFFF、逆順の対・末尾の単独を含む）があるとき、どの context でも INVALID_UNICODE で止めます。U+FFFD に置き換えません。",
          "止める順は context → 文字列か → 長さ → 単独のサロゲートで、最初に当たった 1 つだけを返します。"
        ],
        "what_it_does_not_do": [
          "「injection 防止」を名前に持つ項目ですが、この部品は決めた 1 つの context の中で文字を文字として書く符号化だけで、HTML の無害化（許すタグを残す処理）は含みません。",
          "どの値をどの context に入れるか（テンプレートの組み立て）は決めません。使う側、または別の部品に残ります。",
          "ある context のための符号化は、別の context での安全ではありません。",
          "ブラウザの実際の読み方（parse）は実機で確かめていません。HTML・URL・JSON の仕様の規則から決め、Node.js v24.21.0 の組み込みの関数と独立オラクルで確かめました。",
          "Unicode の正規化はしません。見た目が同じでも、符号単位が違えば別の出力です。",
          "符号化できたことは、その文字列を別の context に入れてよいことでも、HTML として無害にしたことでも、内容を受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のテンプレートエンジンやエスケープのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/encode.js の encode(text, context) を呼びます（投げません）。受け取るのは、プリミティブの文字列 text（1,048,576 UTF-16 符号単位まで）と、html_text・html_attribute_quoted・url_component・json_in_html_script のどれかの context です。返すのは { ok: true, errors: [], text }（符号化した文字列）か { ok: false, errors: [{ code }] }（code は error_taxonomy。text は無い）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-03",
        "category": "I/検証・無害化・安全",
        "kind": "Tsugite",
        "title": "マルウェアスキャン — マルウェア検査 receipt 継手（Tsugite）",
        "summary": "マルウェア検査 receipt 継手（Tsugite）は、アップロードされた添付などを pin した scanner へ提出し、返ってきた検査の受領（ScanReceipt）を、対象の digest・pin した engine と版・検査の時間枠・語彙・契約の const で検める継手（つぎて）です。検査そのものは外部の engine が行い、この継手は scan engine を含みません。どの結果でも検疫は保ち、安全とは言いません（NOT_DETECTED ≠ SAFE。fail-closed）。",
        "problem": "アップロードの検疫で、偽った・古い・すり替えた検査結果を通してしまうこと、そして「検出なし」を「安全」と取り違えて検疫を外してしまうことを防ぎます。検出が無くても安全は導かず（safety: NOT_DERIVED_FROM_SCAN）、検疫の解除は Policy・人の決定に残します。",
        "use_when": [
          "アップロードされた添付などを、外部の scan engine に検査させ、その結果を検めてから扱いたいとき。",
          "検査の受領を、対象の digest・pin した scanner と engine と版・提出からの時間枠で照合し、偽った・古い・すり替えた結果を通したくないとき。",
          "検査の結果にかかわらず検疫を保ち、解除は Policy・人の決定に任せたいとき。",
          "判定の無い・不明な検査結果を、clean 扱いにしたくないとき。"
        ],
        "do_not_use_when": [
          "マルウェアを検査する engine そのものがほしいとき。この継手は scan engine を含みません。検査は外部の engine が行い、どの engine につなぐかはまだ決まっていません。",
          "「検出なし」を「安全」として使いたいとき。NOT_DETECTED でも安全とは言わず（safety: NOT_DERIVED_FROM_SCAN）、検疫を保ちます。",
          "検疫の解除をこの継手にさせたいとき。解除は Policy・人の決定で、この継手は扱いません。",
          "モデレーション（I-04）が要るとき。別の部品です。",
          "検査の結果を、承認や信頼・認可の根拠として扱いたいとき。どの結果も承認ではなく、trust・authorization は導きません（NOT_DERIVED）。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "提出を検めます。許可していない content-type、大きさの上限を超えるもの、中身の改ざん（digest と合わない bytes）は提出しません（REFUSED）。",
          "scanner と engine を検めます。pin していない scanner は受け付けず、pin した一覧に無い engine や版の受領は受理しません。pin していない engine の受領が来れば、以後は停止します（HALTED）。",
          "受領の対象を照合します。request_digest を偽った受領、対象の digest をすり替えた受領は受理せず、以後は停止します（HALTED）。",
          "時間枠を検めます。提出より前の受領と、提出から max_scan_seconds を過ぎた受領は、古い受領として受理しません。",
          "語彙と証拠を検めます。知らない判定、証拠の無い DETECTED、safe・trusted・authorized を主張する受領は受理しません。",
          "INCONCLUSIVE・UNKNOWN の判定は SCAN_INCONCLUSIVE とし、clean 扱いにしません。",
          "scanner の障害は UNKNOWN とします。同じ提出は二重に出さず、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "マルウェアを検査しません。scan engine を含まず、検査そのものは外部の engine が行います。実 scan engine は未定で、port の実装と pin する対象（engine・版・受領の形式）は、接続するときに別の関門で決めます。",
          "検出を受けた後の案件の整理はしません。核（case-support）の役目です。検疫の解除は Policy・人の決定です。",
          "安全を導きません。どの結果にも safety: NOT_DERIVED_FROM_SCAN、quarantine: KEEP_UNTIL_POLICY_DECISION、approval: false が付きます。",
          "検疫を解除しません。解除は Policy・人の決定です。",
          "モデレーション（I-04）は扱いません。別の部品です。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "ウイルス対策の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1 でつなぎます。createScanClient({ cfg, port }) の scan(ScanSubmitRequest, bytes) は、DETECTED_QUARANTINE_KEPT・NOT_DETECTED_QUARANTINE_KEPT・SCAN_INCONCLUSIVE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。利用側が用意するもの: 外部の scan engine への port（submit と、任意で lookup）。副作用は外部への提出（EXTERNAL_REVERSIBLE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-04",
        "category": "I/検証・無害化・安全",
        "kind": "Tsugite",
        "title": "コンテンツ・モデレーション — コンテンツ・モデレーションの継手（Tsugite）",
        "summary": "コンテンツ・モデレーションの継手（Tsugite）は、1 つの中身（投稿・画像など）について、外のモデレーションの事業者・AI に観察を頼む要求（ModerationRequest）を検めてから運び、返った receipt（ModerationReceipt）を契約どおりに照合する継手（つぎて）です。返すラベルは観察だけで、消す・止める・公開する・人の判断・承認は導きません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "外の事業者へ中身を渡すことは取り消せない開示（EXTERNAL_IRREVERSIBLE）です。確かめていないバイト列や、開示の根拠が覆わない事業者・中身の種類へ渡してしまうこと、そして返ったラベルを削除・公開の拒否・人の判断と取り違えることを防ぎます。",
        "use_when": [
          "投稿・画像などの中身を、外のモデレーションの事業者・AI に見てもらい、その観察（ラベル）を判定の素材として受け取りたいとき。",
          "事業者・方針の版・モデルの版を設定で固定し、固定の外の版で観察させたくないとき。版がずれた receipt も受け取りません。",
          "開示の根拠（disclosure_basis_ref）が、その事業者と content_type を覆うときだけ中身を外へ渡したいとき。",
          "送る中身のバイト列が、要求に載せた content_digest と同じものだと確かめてから渡したいとき。"
        ],
        "do_not_use_when": [
          "観察の結果から、削除・公開停止などの処置を自動で行いたいとき。この継手の結果は事実の観測で、処置ではありません（enforcement_action は NOT_DERIVED）。",
          "手元でモデルを動かして観察したいとき。外の事業者・AI に頼む継手で、手元の観察は別の Primitive の範囲です。",
          "中身を外へ渡してはいけない場面。送れば取り消せない開示（EXTERNAL_IRREVERSIBLE）になり、結果の分からない要求（UNKNOWN）でも開示はすでに起きたかもしれません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える事業者への接続がほしいとき。実の事業者・AI には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "設定の固定を検めます。設定が無い・一覧が空・表が欠ける、または事業者・方針・方針の版・モデル・モデルの版が固定の外、ほかの事業者のモデルなら、送りません。",
          "開示の根拠を検めます。設定に無い根拠、その事業者または content_type を覆わない根拠、Ref の形でない根拠では、送りません。",
          "中身のバイト列を検めます。バイト列が無い、sha256 が content_digest と違う、確かめた後に変えられた場合は送りません。要求に中身そのものを載せた要求も拒否します。",
          "参照と要求の形を検めます。空・ドライブの絶対パス・..・先頭の /・制御文字・U+2028/2029 を含む参照、Ref でない request_id、media type でない content_type、追加の欄は送りません。設定が形の悪い参照を固定していても通しません。",
          "receipt を照合します。request_digest の偽り、別の要求・別の事業者の receipt、content_digest の写しの違い、受け取った中身の digest が違うのに CONTENT_MISMATCH でない receipt、方針・モデルの版のずれは受理しません。",
          "ラベルの形を検めます。語彙の外の verdict_state・level、ラベルの無い OBSERVED、OBSERVED でないのにラベルがある receipt、category の重なり、0〜10000 の整数でない score_bp は受理しません。",
          "処置・人の判断・公開の判断を導いた receipt（enforcement_action・human_judgement・publication_decision が NOT_DERIVED でないもの）、状態の許さない効果や UNKNOWN の効果を持つ receipt は受理せず、停止します（HALTED）。",
          "結果の分からない要求は同じ要求の再送（lookup）で解き、解けるまでほかの要求は送りません。同じ request_id の別の要求、未解決の要求のすり替え、送る間に変えられた要求は送りません。"
        ],
        "what_it_does_not_do": [
          "モデレーションの処置をしません。この継手は観察の依頼を運ぶ継手で、削除・公開停止・違反の判断の機能は含みません。結果は事実の観測で、どの結果にも enforcement_action・human_judgement・publication_decision の NOT_DERIVED と approval: false が付きます。",
          "ラベルの正しさ、方針への当てはまり、score_bp の意味は確かめません。観察の中身は事業者の答えです。違反と判断するのは、別の人の判断と核の役目です。",
          "開示の根拠（同意・承認の記録）を作らず、それが有効か・期限内か・撤回されていないかも確かめません。設定に載っていることだけを見ます。根拠は部品の外の人・組織の役目です（APPROVAL_OUTSIDE_PART）。",
          "事業者が実際に受け取ったバイト列は、事業者が received_content_digest を返したときだけ確かめられます。返さなければ、確かめたバイト列を port に渡したところまでです。",
          "時刻（observed_timestamp）は形だけを検め、receipt が古い・使い回しかは見ません。照合の記録と未解決の要求はこの継手の中だけにあり、再起動で消えます。",
          "事業者の認証に使う資格情報は持ちません。資格情報は port 側が持ちます（HELD_BY_PORT）。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のモデレーション API の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/content-moderation/v1 でつなぎます。createModerationClient({ cfg, port }) の submit(request, bytes) に ModerationRequest と中身のバイト列を渡すと、DONE（result に verdict_state・labels ほか）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。cfg には固定の一覧 4 つ（allowed_providers ほか）が要ります。利用側が用意するもの: 事業者への port（submit、任意で lookup）と資格情報。副作用: EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-05",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "ファイル型検証 — ファイル型検証の部品（Primitive）",
        "summary": "ファイル型検証の部品（Primitive）は、bytes の先頭の magic bytes を版つきの表（pdf・png・jpeg・gif・zip・gzip・webp の 7 つ）と照らして型と MIME を判り、申告の MIME・許す MIME の一覧と突き合わせる単独の部品です。受け取るのは bytes だけです。判らない型・申告との違い・許していない型は、決まったコードで止めます（fail-closed）。",
        "problem": "偽装した拡張子や申告の Content-Type を信じて、実体の違うファイルを受け取ってしまうことを防ぎます。bytes から判った型と申告を突き合わせ、許す一覧を必須にして、表に無い MIME を一覧に書いた設定の誤りも止めます。",
        "use_when": [
          "受け取ったファイルの bytes から、先頭の magic bytes で型と MIME を判りたいとき（detect）。",
          "申告の MIME（declared_mime）が bytes の実体と完全に一致し、許す MIME の一覧（allowed_mimes）にあるときだけ受け取りたいとき（check）。",
          "大きなファイルでも一定の手間で検めたいとき。読むのは先頭の 12 bytes までです。",
          "例外を投げない呼び方が要るとき。check は { ok, errors, detected } を返し、例外を投げず、options の getter を呼びません。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として手元で呼びたいとき。"
        ],
        "do_not_use_when": [
          "中身の安全（マルウェア・悪意のある PDF の JavaScript・zip bomb など）を検めたいとき。それは I-03 の範囲です。",
          "ファイルの残りが正しい形か（壊れていないか・開けるか）を確かめたいとき。判るのは先頭の magic bytes だけです。",
          "docx・xlsx など zip を土台にした形式の中の形式を判りたいとき。application/zip とだけ判り、中の形式の MIME を申告すると TYPE_MISMATCH になります。",
          "表の 7 つのほかの型（text・json・svg・mp4・webm・tiff・bmp・heic・7z・rar など）を扱いたいとき。UNKNOWN_TYPE で止めます。",
          "申告の MIME の大文字・引数（; charset= など）・別名（image/jpg など）の読み替えを任せたいとき。読み替えはしないので、呼ぶ側が外して小文字にしてから渡します。",
          "bytes の大きさの上限が要るとき。上限は持たず、I-06 の部品か使う側で決めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "bytes（Uint8Array）でないとき（文字列・数の配列・ArrayBuffer・ほかの型付き配列）、check は NOT_BYTES で止めます（detect は FileTypeError を投げます）。",
          "表のどの型の magic bytes とも合わないとき（途中で切れた頭・1 byte 違い・GIF88a・小文字の %pdf-・zip の空の書庫や分割の印・gzip の別の方式・RIFF の WAVE・位置のずれ）、UNKNOWN_TYPE で止めます。",
          "判った MIME が declared_mime と完全には一致しないとき（別名・引数付き・大文字・zip に Office の MIME を申告）、TYPE_MISMATCH で止めます。",
          "判った MIME が allowed_mimes に無いとき、NOT_ALLOWED で止めます。",
          "allowed_mimes が無い・空・穴がある・配列に似たオブジェクト・表に無い MIME を含むとき、INVALID_OPTIONS で止めます。",
          "declared_mime が文字列でないとき、opts に知らないキー（symbol を含む）・getter があるとき、Proxy の罠・クラスのインスタンスのとき、INVALID_OPTIONS で止め、例外を投げません。",
          "止めるときのエラーはいつも 1 つで、検める順番は bytes・options・型が判るか・申告・許す一覧です。"
        ],
        "what_it_does_not_do": [
          "型が判ることは、中身の安全の検めではありません。マルウェアの検査（I-03）・大きさの上限（I-06）・中の構造の検め（画像の復号など）は別の部品で、組み合わせるのは使う側です。",
          "magic bytes が合っても、ファイルの残りが正しい形である証明ではありません。PDF の xref・PNG の chunk・zip の中央ディレクトリなどは読みません。",
          "継手の中にある magic bytes の写し（content.js 2 つ）を、まだこの部品に置き換えていません。pdf・png・jpeg の検めは作業場で 1 回突き合わせて違いは無かったものの、写しは text/plain・application/json も扱い Buffer だけを受け取る点が違い、置き換えは別の作業です。",
          "zip を土台にした形式の中の形式、空の zip・分割の zip は判りません。",
          "型が判ったことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のファイル型判定ライブラリ（file-type・libmagic など）の写しや使い方の雛形ではありません。"
        ],
        "integration": "src/filetype.js の check(bytes, opts) を呼びます（投げません）。受け取るのは bytes（Uint8Array。Buffer を含む）と、allowed_mimes（必須）と declared_mime（任意）だけの素のオブジェクト opts です。返すのは { ok: true, errors: [], detected: { type, mime } } か { ok: false, errors: [{ code }], detected }（code は error_taxonomy）。detect(bytes) は { type, mime } か null を返します（bytes でなければ投げます）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-06",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "サイズ・数量制限 — サイズ・数量制限の部品（Primitive）",
        "summary": "サイズ・数量制限の部品（Primitive）は、値の大きさを 6 つの決まった単位（bytes・utf8_bytes・utf16_units・code_points・items・keys）のどれかで数え、上限・下限と比べる単独の部品です。単位ごとに受け取る値の型が決まっていて、合わなければ止めます。上限は必須で、超えれば TOO_LARGE、足りなければ TOO_SMALL で止めます（fail-closed）。単位は呼ぶ側が決めて渡します。",
        "problem": "過大な入力を断る上限の検めで、数えられない値を 0 と数えてしまうこと、文字列を bytes として・配列に似たオブジェクトを配列として数えてしまうこと、単独のサロゲートを含む文字列を UTF-8 の長さで黙って数えてしまうこと、上限の無い・空の範囲を受け取ってしまうことを防ぎます。",
        "use_when": [
          "本文・名前・配列・オブジェクトの大きさに、使う側が決めた単位と上限を当てたいとき（例: 本文は utf8_bytes で 1 MiB、名前は code_points で 64）。",
          "同じ文字列でも単位によって数が違うことを、単位を明示して扱いたいとき（絵文字 1 つは utf8_bytes で 4、utf16_units で 2、code_points で 1）。",
          "上限・下限の境目を決めて検めたいとき。上限は既定で含み（max_inclusive）、下限は既定 0 で常に含みます。",
          "I-08 の正準化・B-07 のハッシュ・I-05 のファイル型の部品に渡す前に、大きさの上限を当てたいとき。それらの部品は大きさの上限を持ちません。",
          "例外を投げない呼び方が要るとき。check は例外を投げず、値と options を書き換えず、getter を呼びません。",
          "時計・乱数・環境の locale に左右されず、状態を持たない処理として、手元で呼びたいとき。"
        ],
        "do_not_use_when": [
          "見た目の 1 文字（書記素。👨‍👩‍👧 や 🇯🇵）の数で制限したいとき。書記素は数えません。",
          "見た目が同じ文字列を同じ長さとして扱いたいとき。Unicode の正規化はしないため、é と e + 結合文字は数が違います。",
          "入れ子の中（配列の中の配列、オブジェクトの中のキー）まで含めた大きさで制限したいとき。items・keys は一段目だけを数えます。",
          "値をメモリーに読み込む前に、大きすぎる入力を止めたいとき。この部品は、すでにメモリーにある値を数えます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "大きさが上限を超えるとき（max_inclusive が false なら上限と等しいときも）TOO_LARGE、下限より小さいとき TOO_SMALL で止め、数えた大きさ（size）を返します。",
          "値の型が単位に合わないとき（bytes に文字列・ArrayBuffer・ほかの型付き配列、文字列の単位に bytes・String オブジェクト、items に配列に似たオブジェクト・Set、keys に配列・Map・Date・クラスのインスタンスなど）、VALUE_TYPE_MISMATCH で止めます。",
          "utf8_bytes で、文字列に単独のサロゲート（対になっていない U+D800〜U+DFFF）があるとき、INVALID_UNICODE で止めます。",
          "unit が 6 つの単位のどれでもないとき（大文字・別名を含む）、UNKNOWN_UNIT で止めます。",
          "max が無い、max・min が 0 以上の安全な整数でない、min > max、または受け取れる大きさが 1 つも無い範囲のとき、INVALID_LIMITS で止めます。",
          "opts が素のオブジェクトでない、知らないキー・getter がある、max_inclusive が真偽値でないとき INVALID_OPTIONS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。"
        ],
        "what_it_does_not_do": [
          "名前の「過大入力の遮断」のうち、大きすぎる値の読み込みそのものは止められません。値を渡す前にメモリーに載せる手間は、この部品の外の話です。",
          "どの単位で何を上限にするかは決めません。使う側（各継手・契約・Kernel の方針）が決めます。",
          "書記素の数・Unicode の正規化・入れ子の中の大きさは扱いません。",
          "継手の中の大きさの上限の検めと、工房の台帳の I-06 の記録を、この部品に置き換えてはいません。置き換えは別の作業です。utf8_bytes は、単独のサロゲートのある文字列では Buffer.byteLength と違い、止めます。",
          "Proxy の罠は単体試験の数件だけで確かめました（ファズには入れていません）。",
          "範囲の中に収まったことは、値の中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の検証ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/size.js の check(value, opts) を呼びます（投げません）。opts は { unit, max, min?, max_inclusive? } だけの素のオブジェクトで、unit は 6 つの単位のどれか、max は必須の 0 以上の安全な整数です。返すのは { ok: true, errors: [], size } か { ok: false, errors: [{ code }], size }（code は error_taxonomy、size は TOO_LARGE・TOO_SMALL のときだけ数、ほかは null）。measure(value, unit) は 0 以上の整数を返し、受け取れない値で SizeError を投げます。副作用（NONE）・状態・外の接続・資格情報・port は無く、依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-07",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "重複排除 — 重複排除の部品（Primitive）",
        "summary": "重複排除の部品（Primitive）は、渡された一覧（{ key } の配列）と、渡された索引（known_keys）の中で、同じ key の重複を見分ける単独の部品です。はじめて出た番号を kept に、2 回目からと索引にあるものを duplicates（{ index, of }）に分けます。key は UTF-16 の符号単位のまま比べます。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
        "problem": "reconcile と対になる重複の見分けで、形の違う entry を黙って読み飛ばすこと、__proto__・constructor などの名前の key を取り違えること、件数や key の長さに上限の無い入力を受け取ることを防ぎます。同じ入力からは、いつでも同じ答えになります。",
        "use_when": [
          "1 回の呼び出しで渡す一覧の中で、同じ key の重複を見分けたいとき。どの番号も kept と duplicates のどちらか一方にだけ、1 回入ります。",
          "前に見た key を known_keys として渡し、それとの重複も of: \"known\" として見分けたいとき。",
          "I-08 の正準化や B-07 のハッシュで作った key を、符号単位の完全一致で比べたいとき。どの値から key を作るかは使う側が決めます。",
          "受け取れない入力を、黙って読み飛ばさずに、コードと位置（JSON Pointer）で止めたいとき。",
          "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。"
        ],
        "do_not_use_when": [
          "保存済みの索引や、ほかの呼び出しとの重複を見たいとき。比べるのは引数の中だけで、持続する索引・大域の索引は読まず書きません（保管の継手の側）。",
          "見た目が同じ key（é と e + 結合文字、A と a、前後の空白の違い）を同じものとして扱いたいとき。正規化・大文字小文字の同一視・空白の除去はしません。同じにしたいなら、使う側が決まった規則で key を作ってから渡します。",
          "100,000 件を超える一覧を 1 回で扱いたいとき。使う側で分けます。分けた呼び出しの間の重複は、前の key を known_keys に渡さない限り見分けません。",
          "entry の中身（key のほかのプロパティ）で重複を判断したいとき。重複の判断は key だけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "entries が素の配列でないとき（配列の派生クラスを含む）INVALID_ENTRIES、100,000 件を超えるとき TOO_MANY_ENTRIES で止めます。",
          "要素が素のオブジェクトでない（穴・getter・クラスのインスタンス・null prototype を含む）、自分のデータのプロパティ key が無い（getter・継いだもの）とき、MALFORMED_ENTRY で止めます。",
          "key が文字列でない・空のとき INVALID_KEY、4096 の UTF-16 の符号単位を超えるとき KEY_TOO_LONG で止めます。",
          "known_keys が素の配列でないとき INVALID_KNOWN_KEYS、100,000 件を超えるとき TOO_MANY_KNOWN_KEYS、同じ key が 2 回あるとき DUPLICATE_KNOWN_KEY（2 回目の位置）で止めます。",
          "options が素のオブジェクトでない、known_keys のほかの名前がある、known_keys が getter のとき INVALID_OPTIONS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
          "止めたときは、最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、kept・duplicates は返しません。"
        ],
        "what_it_does_not_do": [
          "名前の「重複排除」のうち、保存されたデータから重複を消すことはしません。引数の一覧の番号を kept と duplicates に分けるだけです。保存済みの索引を引く・書くのは保管の継手の側です。",
          "名前の「同一物の検出」のうち、中身が同じかどうかは見ません。key の符号単位の完全一致だけで、正規化・大文字小文字の同一視もしません。",
          "渡されていない索引（ほかの呼び出し・保存済みのもの）との重複は分かりません。",
          "上限ちょうどの 100,000 件を受け取ることは、単体試験だけで確かめました（ファズは上限を超えるものだけ）。",
          "重複でないことは、中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の重複排除のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/dedupe.js の dedupe(entries, options) を呼びます（投げません）。受け取るのは、{ key } の素のオブジェクトの素の配列（100,000 件まで）と、省略か { known_keys } だけの options です。返すのは { ok: true, errors: [], kept, duplicates }（kept は番号の小さい順、duplicates は { index, of } で of は番号か \"known\"）か { ok: false, errors: [{ code, path }] }（code は error_taxonomy、path は JSON Pointer）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するもの（port）はありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-08",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "正準化 — 正準化の部品（Primitive）",
        "summary": "正準化の部品（Primitive）は、JSON の値を RFC8785-restricted の規則で正準の文字列（UTF-8 bytes）にする単独の部品です。キーを UTF-16 の符号単位の順に並べ、空白を入れず、数は安全な整数だけを受け取ります。受け取れない値は、決まったコードと位置で止めます（fail-closed）。",
        "problem": "署名・突合の前処理で、キーの順・空白・数の書き方のゆれや、受け取れない値を黙って読み飛ばすことによって、digest の元になる bytes が変わってしまうことを防ぎます。同じ値からは、いつでも同じ文字列ができます。",
        "use_when": [
          "署名・突合の前処理として、JSON の値から digest の元になる正準の bytes を作りたいとき（canonicalBytes）。",
          "受け取れない値（小数・undefined・関数・素でないオブジェクト・穴のある配列など）を、黙って読み飛ばさずに、コードと位置（JSON Pointer）で止めたいとき。",
          "例外を投げない呼び方が要るとき。tryCanonicalize は { ok, errors, text } を返し、例外を投げません。",
          "時計・乱数・環境の locale に左右されない、決定的な出力が要るとき。",
          "外の境界・状態を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
        ],
        "do_not_use_when": [
          "小数・指数の数を含む値を正準化したいとき。数は安全な整数（-(2^53-1)〜2^53-1）だけで、RFC 8785 の全体ではありません。",
          "見た目が同じ文字列を同じものとして扱いたいとき。Unicode の正規化はしないため、é と e + 結合文字は別の文字列です。",
          "カタログの一言「表現ゆれの統一（RFC8785 等）」のような広い統一を求めるとき。この部品がするのは、RFC 8785（JCS）のうち数を安全な整数に限った profile の範囲です。",
          "1000 段を超える入れ子の値を扱うとき。TOO_DEEP で止めます。",
          "値の大きさの上限が要るとき。文字列・配列・キーの長さの上限は持たず、大きさの上限は I-06 の部品か使う側で決めます。",
          "Node.js v24.21.0 のほかの版・エンジンで使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "小数・安全な範囲の外の整数・NaN・Infinity のとき、NOT_A_SAFE_INTEGER で止めます。",
          "undefined（値そのもの・配列の要素・オブジェクトのメンバー）のとき UNDEFINED_VALUE、関数・symbol・bigint のとき UNSUPPORTED_TYPE で止めます。",
          "配列の派生クラス、穴のある配列、要素のほかのプロパティを持つ配列のとき、NOT_A_PLAIN_ARRAY・SPARSE_ARRAY・EXTRA_ARRAY_PROPERTY で止めます。",
          "素でないオブジェクト（Date・Map・Buffer・null prototype・クラスのインスタンスなど）、symbol のキー、列挙できないプロパティを持つオブジェクトのとき、NOT_A_PLAIN_OBJECT・SYMBOL_KEY・NON_ENUMERABLE_PROPERTY で止めます。",
          "getter・setter のプロパティがあるとき、呼ばずに ACCESSOR_PROPERTY で止めます。値を調べる間に中身が変わりません。",
          "入れ子が 1000 段を超えるとき、スタックを溢れさせずに TOO_DEEP で止めます。",
          "値を調べること自体が例外になるとき（Proxy の罠など）、tryCanonicalize は INSPECTION_FAILED を返し、例外を投げません。",
          "止めたときは、コード（code）と止めた位置（path。JSON Pointer（RFC 6901）で、根は空文字）を返します。"
        ],
        "what_it_does_not_do": [
          "RFC 8785 の全体（小数・指数の数の書き方）は扱いません。数は安全な整数だけです。",
          "Unicode の正規化はしません。見た目が同じでも、符号単位が違えば別の文字列です。",
          "どのフィールドを digest に入れるか・どの値を除くかは決めません。使う側（各継手・契約）に残ります。",
          "継手の中にある正準化の写し（20 個）を、まだこの部品に置き換えていません。写しが正しく扱う値では byte 単位で同じ出力（または同じく止める）ですが、写しが壊れた JSON や欠けた出力を作る値（穴のある配列・symbol のキーなど）は、この部品は止めます。写しの側の扱いはそのままで、置き換えは互換表を確かめた後の別の作業です。",
          "正準の文字列ができたことは、値の中身が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "Node.js v24.21.0 のほかのエンジンでの、JSON.stringify の文字列の書き方は確かめていません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の JSON ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/canonical.js の tryCanonicalize(value) を呼びます（投げません）。canonicalize・canonicalBytes は正準の文字列・UTF-8 の Buffer を返し、受け取れない値で CanonicalizationError を投げます。受け取るのは JavaScript の値 1 つ（null・真偽値・文字列・安全な整数・密な配列・素のオブジェクトの入れ子）。返すのは { ok: true, errors: [], text } か { ok: false, errors: [{ code, path }] }（code は error_taxonomy、path は JSON Pointer）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-09",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "可読性の検め — 可読性の検め（gzip・JSON・TSV・PDF／画像の浅い検め）の部品（Primitive）",
        "summary": "可読性の検めの部品（Primitive）は、取得した bytes が開けるか（gzip の解凍、UTF-8、JSON の読み、TSV の列の数、PDF・PNG・JPEG の印）を検め、事実として返す単独の部品です。読めないことはエラーではなく、readable: false と reason の事実で返します。開けないものを止める判断（門番）はしません。",
        "problem": "読めないファイルは、開くまで気づきません。この部品は、壊れた gzip・不正な UTF-8・壊れた JSON でも例外を投げずに事実として返し、解凍した後の大きさを 1 GiB で止めて、gzip の爆弾で際限なくメモリーを使わないようにします。format を中身から推し量らないので、宣言と違う中身を別の型として読み替えません。",
        "use_when": [
          "取得した bytes が、宣言された型として開けるかを事実として残したいとき（probeReadability）。",
          "gzip で圧縮された bytes を、解凍してから検めたいとき（compression: \"gzip\"。解凍できなければ GUNZIP_FAILED）。",
          "JSON の最上位の型（top_type）や、TSV の行の数と列の数（rows・columns）を事実として知りたいとき。",
          "PDF・PNG・JPEG に印があるか（PNG は IHDR の width・height・bit_depth・color_type も）を浅く確かめたいとき。",
          "読めないことをエラーにせず、なぜ読めなかったか（reason と、TSV_RAGGED のときの行番号など）を受け取りたいとき。",
          "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。ファイルは開かず、bytes は呼ぶ側が渡します。"
        ],
        "do_not_use_when": [
          "PDF・画像が描けること、ページ・画像のデータが壊れていないこと、暗号化されていないことを確かめたいとき。readable: true は印があることだけです（markers_only: true）。",
          "UTF-8 のほかの文字コード（Shift_JIS など）を読みたいとき。charset が utf-8 でなければ CHARSET_NOT_SUPPORTED で、変換も推測もしません。charset を宣言しない Shift_JIS は NOT_UTF8 になります。",
          "中身の先頭の bytes から型を当ててほしいとき。format は宣言された media type の type/subtype だけで決めます。",
          "TSV の引用符（\"…\" の中の \\t や改行）を扱いたいとき。\\t は常に区切りとして数えます。",
          "PDF の前に bytes がある、%%EOF の後に 1024 bytes を超える bytes が続く、JPEG の EOI の後に bytes が続くファイルを、開けるものとして扱いたいとき。印が無いという事実になります。",
          "256 MiB を超える入力や、解凍して 1 GiB を超える入力を検めたいとき。INPUT_TOO_LARGE（limit 付き）で止めます。これは読めないという事実ではなく、検めなかったという意味です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "入力がオブジェクトでない、bytes が Uint8Array でない（文字列・数の配列・ArrayBuffer・ほかの型付き配列）、プロパティを読むこと自体が例外になるとき、NOT_BYTES で止めます。",
          "declared_media_type が契約 raw-fetch/v1 の形の文字列でない・255 文字を超えるとき INVALID_MEDIA_TYPE、compression が \"gzip\" でも \"none\" でもないとき（省略を含む）INVALID_COMPRESSION で止めます。",
          "宣言の引数が RFC 9110 の parameter として読めない・charset が 2 つ以上あるとき MEDIA_TYPE_PARAMETERS_UNREADABLE、json・tsv・text で charset が utf-8 でないとき CHARSET_NOT_SUPPORTED を、bytes を読む前に返します。",
          "UTF-8 は fatal に復号し、過長・サロゲート・U+10FFFF 超・途中で切れた並びは NOT_UTF8 です。先頭の BOM は外さず、BOM 付きの JSON は JSON_PARSE_FAILED です。",
          "空でない行の列の数がそろわない TSV は TSV_RAGGED で、最初にそろわなかった行の番号と列の数を facts に返します。",
          "PDF は先頭の %PDF- と最後の 1024 bytes の中の %%EOF、PNG は 8 bytes の署名・長さ 13 の IHDR・その CRC-32、JPEG は先頭の FFD8（SOI）と末尾の FFD9（EOI）を検めます。無ければ PDF_MARKERS_MISSING・PNG_SIGNATURE_OR_IHDR_BAD・JPEG_MARKERS_MISSING です。",
          "入力が 256 MiB を超える、解凍した後が 1 GiB を超える、JSON の本文が 256 MiB を超えるときは、検めずに INPUT_TOO_LARGE と limit（input・gunzip_output・json_text）を返します。",
          "宣言された media type が対応の表に無いときは FORMAT_UNKNOWN です。bytes は渡された view の範囲だけを読みます。"
        ],
        "what_it_does_not_do": [
          "readable は開けるかどうかの事実だけで、中身が正しいこと・完全なこと・安全なこと・受け入れてよいことを意味しません。pdf・png・jpeg では印があることだけを意味します。",
          "開けないものを止める判断（門番）はしません。止めるかは使う側が決めます。",
          "ファイルを開く・取得する・保存することはしません。",
          "gzip の解凍は zlib に任せ、ファズのオラクルも zlib を使います。zlib そのものの正しさは、この部品の試験の範囲の外です。",
          "メモリーが足りない環境での振る舞いは確かめていません。JSON.parse が文法以外の理由で失敗した場合も JSON_PARSE_FAILED になります。",
          "可読性の結果は、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のファイル形式のライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/decode-probe.js の probeReadability({ bytes, declared_media_type, compression }) を呼びます（投げません）。bytes は Uint8Array（256 MiB まで）、declared_media_type は解凍した後の中身の型の宣言（契約 raw-fetch/v1 の形、255 文字まで）、compression は \"gzip\" か \"none\"。返すのは { ok: true, errors: [], readable, format, facts, reason } か { ok: false, errors: [{ code }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みの zlib だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-10",
        "category": "I/検証・無害化・安全",
        "kind": "Component",
        "title": "取得データの検査（版つき） — 取得データの検査（3 段・4 状態・版つき）の部品（Component）",
        "summary": "取得データの検査の部品（Component）は、取得の直後に raw を 3 段（完全性・可読性・実用性）で検め、4 つの状態（done・need_input・failed・need_consent）のどれかを、検査器の版つきの別の記録に残す部品です。記録の検め（部品(Primitive) N-09）・検めた読出し（C-01 の継手）・可読性の検め（部品(Primitive) I-09）・source ごとの抽出器・冪等書込（C-05 の継手）を束ねます。raw へは読むだけで、消さない・止めません。done は 3 項目が取り出せたことで、正しいことではありません。",
        "problem": "取り込みの成否は、後から見返せません。この部品は、検査の結果を検査器の版つきの別の記録に残します。あわせて、検査が raw を止めてしまうこと、port の障害なのに嘘の done を書くこと、認定の一覧に無い継手の取得を読むこと、取り出した値（PII を含みうる）を結果に移すことを防ぎます。",
        "use_when": [
          "取得の直後に、fetch.json の記録（fetch_record）とフォルダー（folder_ref = raw/<source>/<YYYYMMDDTHHMMSSZ>）を渡し、検査の結果を probe/<source>/<folder_stamp>/<probe_version>.json に 1 回だけ書きたいとき（probeFetch）。",
          "取得の失敗・記録が無い・N-09 が記録を受け取らないときは failed、tsugite_id が認定の一覧（certified_tsugite）に完全一致しないときは need_consent として、raw を読まずに記録したいとき。",
          "完全性を期待する sha256 と大きさで、可読性を I-09 で、実用性を source ごとの抽出器で 3 項目（取引年月日 occurred_on・金額 amount・相手先 counterparty）が取り出せたかで検め、足りない段を need_input と理由で知りたいとき。",
          "検査器を新しい版にして過去の取得に当て直し、古い記録を残したまま新しい記録を作りたいとき。結果のキーは probe_version を含みます。",
          "need_input の山から、次に作る継手と検査器を見つけたいとき（抽出器が無い source は need_input / NO_EXTRACTOR）。",
          "結果の分からなかった書込を、同じ要求で送り直したいとき（resolvePending）。"
        ],
        "do_not_use_when": [
          "done を、取り出した値が正しいこと・形が正しいこと（日付・金額の書き方）として使いたいとき。done は 3 項目が取り出せたことだけです。",
          "検査を門番にして、合わない raw を止めたり消したりしたいとき。この部品は raw を書かず・消さず・止めません。結果は別の記録です。",
          "ファイルが描けること・壊れていないことまで確かめたいとき。可読性は I-09 の浅い検め（pdf・png・jpeg は印があるか）で、I-09 の上限を超えたファイルは検めていません（READABILITY_NOT_CHECKED）。",
          "抽出器が正しく読んでいるか、認定の一覧が正しいかまで任せたいとき。どちらもこの部品の外で、この部品は tsugite_id の文字列の完全一致だけを見ます。",
          "application/gzip の中身の型を確かに当てたいとき。中身の型は名前の拡張子の表から引くので、表に無い・名前が嘘のときは違う型で検め、need_input になることがあります。",
          "信頼できない抽出器を隔離して動かしたいとき。抽出器は同じプロセスで動き、時間とメモリに上限はありません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違うとき INVALID_OPTIONS、probeFetch の入力の形が違うとき（folder_ref の形・fetch_failed が真偽値でない など）INVALID_INPUT、getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "N-09 の port が投げた・答えが食い違うとき（この記録についての答えでない、schema・区画・files の形が違う）CHECK_PORT_FAILED、記録の source と folder_stamp が folder_ref と違うとき FOLDER_MISMATCH で、結果を書きません。",
          "完全性の段で、保管物が無いとき INTEGRITY_MISSING、GET_REJECTED か自分で計算し直した sha256 が違うとき INTEGRITY_DIGEST_MISMATCH、大きさが byte_size と違うとき INTEGRITY_SIZE_MISMATCH として、全ファイルを並べて need_input にします。",
          "完全性の段の読出しの結果が分からないとき READ_UNKNOWN、断り・停止・形の違い・bytes でない値のとき READ_FAILED、I-09 の port が投げた・答えの形が違うとき READABILITY_PORT_FAILED で止め、結果を書きません（errors[0].name にそのファイル）。",
          "可読性の段で、読めないファイルがあれば need_input / UNREADABLE、検めていないものがあれば need_input / READABILITY_NOT_CHECKED です。実用性の段で、抽出器が投げた・配列でない答えなら EXTRACTOR_FAILED、記録が 0 件なら NO_RECORDS、3 項目の欠けがあれば MISSING_FACTS（件数と欠けた項目の名前）です。",
          "同じ版の結果のキーに、このインスタンスが前に別の中身を送っているとき PROBE_RESULT_EXISTS で送りません。書込の結果が分からないとき PROBE_WRITE_UNKNOWN（保留の間は PROBE_PENDING）、送らなかったとき WRITE_REFUSED、停止・結果が APPLIED か DEDUP_HIT でないとき WRITE_FAILED です。",
          "timestamp が RFC 3339 の UTC（Z）の実在する日時を返さないとき CLOCK_FAILED で何も読まず書かず、前の操作が終わっていないとき BUSY、保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。"
        ],
        "what_it_does_not_do": [
          "取得データの検査の部品は継手ではありません。自分の Request/Receipt は無く、結果の書込という外への効果は、束ねた C-05 の継手が自分の契約で起こします（raw の読出しは NONE）。この部品自身は外へ触れません。",
          "束ねた部品の意味（記録を受け取れた ≠ 取得の正しさ・読めた ≠ 中身の正しさ・開けた ≠ 壊れていない・書けた ≠ 受け入れ）は、それぞれの部品の側に残ります。抽出器そのものと取り出した値の正しさは、この部品に入りません。",
          "結果は承認ではなく、正しいことも示しません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "取り出した値は結果の記録に書きません。書くのは件数と欠けた項目の名前とファイル名・番号だけです。",
          "raw のキーがその区画の保管物を指すかは backend の設定の役目です。ほかのプロセスが書いた同じ版の結果は、この部品ではなく継手と backend が断ります（どちらでも上書きはしません）。",
          "本物の N-09・I-09・C-01・C-05 には、この部品の試験では接続していません。単体試験とファズは代役です。状態（送った結果のキーの控え・保留）はプロセスの中だけで、再起動で消えます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のデータ品質ツールの雛形ではありません。"
        ],
        "integration": "src/probe.js の createProbe(options) で作り、{ ok, errors, instance } を返します。options は probe_version・certified_tsugite・result_target・request_prefix・ports・任意の timestamp です。probeFetch は state・reason・result・result_key などを返し、結果に approval: false が付きます。port は checkFetchRecord（N-09）・probeReadability（I-09）・区画ごとの read.<class>.get（C-01 の継手のクライアント）・write.submit（C-05 の継手のクライアント）・任意の extractors です。資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "I-11",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "選んだ入力の組み立てと UTF-8 の容量 — 選んだ入力の組み立てと UTF-8 の容量の部品（Primitive）",
        "summary": "選んだ入力の組み立てと UTF-8 の容量の部品（Primitive）は、明示して選んだ項目だけを、選んだ順に決まった区切りでつなぎ（prefix + 部分.join(separator) + suffix）、UTF-8 の bytes を数えて、使う側が渡した上限と比べる単独の部品です。選ぶときは版を名指しし、範囲は単位（UTF16 か CODE_POINT）を名指しします。超えたら OVER_LIMIT と超えた量を返し、切りません。受け取れない入力は、決まったコードと位置で止めます（fail-closed）。",
        "problem": "送った文が何から組み立てられ、上限に入ったかは、後から見えません。この部品は、選んでいない項目を足すこと、古い版の選択を使うこと、上限を超えた文を黙って切る・縮める・分けることを防ぎ、選んだもの・除いたもの・版・digest の一覧を返します。",
        "use_when": [
          "明示して選んだ項目（id と revision を名指し）だけを、選んだ順に separator でつなぎ、前後に prefix・suffix を付けた request を組み立てたいとき（assemble）。選んでいない項目は入りません。",
          "組み立てた request の UTF-8 の bytes を、使う側が渡す上限 limit_bytes（1〜1,073,741,824）と比べたいとき。上限ちょうどは WITHIN_LIMIT です。上限の既定値は持たず、使う側が渡します。",
          "項目の一部だけを、単位を名指しした範囲 { unit, start, end } で選びたいとき。unit は UTF16 か CODE_POINT で、同じ数でも別の文字を指しうるので、単位を推し量りません。",
          "何を組み立てたかを後から照らしたいとき。request_digest（request の UTF-8 の sha256）と、版・中身・範囲・区切りのどれかが変われば変わる selection_digest、選んだ順の included と、選ばなかった excluded を返します。",
          "役ごとの投影（P-12）の後に、送る文字列を固定したいとき。値 1 つの大きさを数える I-06 とは違い、選んだものだけを組み立てて版と digest を固定します。",
          "例外を投げず、入力を書き換えず、状態を持たない処理として、手元で呼びたいとき。同じ入力には同じ答えを返します。"
        ],
        "do_not_use_when": [
          "上限を超えた文を切ったり縮めたり分けたりして収めたいとき。OVER_LIMIT のとき request_text は null で、どの部分も切らず除きません。選び直しは使う側です。",
          "どれを選ぶか（選ぶ権限・同意・目的）を部品に確かめさせたいとき。選ばれたものを組み立てるだけで、選ぶ権限・同意・目的が正しいかは見ません。",
          "JSON などの外の包みやフレームを含めた大きさを数えたいとき。数えるのは prefix + 部分.join(separator) + suffix だけで、外の包みは使う側が別に検めます。",
          "組み立てた文を送ったり保存したりしたいとき。この部品は送信・保存をしません。外の包みの組み立てと送信は使う側に残ります。",
          "items・selection が 10,000 件を超える、text・区切りが 4,194,304 の UTF-16 の符号単位を超える入力を扱いたいとき。上限を超えると決まったコードで止めます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が素のオブジェクトでない、知らない名前・getter がある、limit_bytes か separator が無いとき INVALID_OPTIONS、limit_bytes が 1〜1,073,741,824 の整数でないとき INVALID_LIMIT、separator・prefix・suffix が文字列でない（箱の文字列を含む）か長すぎるとき INVALID_FRAMING で止めます。",
          "items が素の配列でないとき INVALID_ITEMS、10,000 件を超えるとき TOO_MANY_ITEMS、要素の形が違うとき MALFORMED_ITEM、id・revision・text の形が違うとき INVALID_ID・INVALID_REVISION・INVALID_TEXT、text が長すぎるとき TEXT_TOO_LONG、同じ id が 2 回あるとき DUPLICATE_ITEM で止めます。",
          "selection の形が違うとき INVALID_SELECTION・TOO_MANY_SELECTED・MALFORMED_SELECTION、選んだ id が items に無いとき UNKNOWN_ITEM、選んだ版が items の版と違うとき REVISION_MISMATCH（古い選択を使いません）、同じ id を 2 回選んだとき DUPLICATE_SELECTION で止めます。",
          "range が { unit, start, end } でない、unit が UTF16・CODE_POINT でない（小文字を含む）、0 ≤ start < end の安全な整数でないとき INVALID_RANGE、end が text の長さを超えるとき RANGE_OUT_OF_BOUNDS、UTF16 の範囲の端がサロゲートの対の間にあるとき RANGE_SPLITS_SURROGATE で止めます。",
          "選んだ部分か separator・prefix・suffix に単独のサロゲートがあるとき（UTF-8 にできない）INVALID_UNICODE、組み立てた request が空か空白（改行・全角の空白を含む）だけのとき BLANK_REQUEST、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
          "止めたときは、決まった順で最初に見つけた 1 件のコードと位置（path。JSON Pointer）を返し、組み立てた結果は返しません。",
          "結果を、別の書き方の独立オラクル（bytes は TextEncoder、符号位置は Array.from、単独のサロゲートは isWellFormed、項目は Map を使わず前から探す）と突き合わせるファズ（1,000,000 回）で、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "WITHIN_LIMIT は「渡された上限に入る」だけで、送ってよい・同意があるという意味ではありません。外の包み（JSON・フレーム）の大きさは数えていません。",
          "中身の digest（content_digest）は渡された text の digest で、元の資料と同じかは見ません。",
          "上限ちょうどの 10,000 件・4,194,304 の符号単位を受け取ることは、単体試験だけで確かめました（ファズは小さな入力だけ）。",
          "外の境界・状態・時計・乱数を持たず、送信・保存をしません。副作用は NONE で、資格情報も使いません。",
          "組み立てと数えの結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "入出力の形は v1 で定めたもので、既存の実装はありません（新規）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のプロンプト組み立てのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/selected-input.js の assemble(items, selection, options) を呼びます（投げません）。items は { id, revision, text }、selection は { id, revision, range? } の配列、options は { limit_bytes, separator, prefix?, suffix? } です。返すのは { ok: true, errors: [], status, request_text, request_digest, selection_digest, included, excluded など } か { ok: false, errors: [{ code, path }] } です。副作用・状態・外の接続・資格情報・依存パッケージ・port は無しです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-01",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "PDF 生成 — 文書・メディア変換継手（Tsugite）",
        "summary": "PDF 生成の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へPDF 生成の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。PDF を作るのは backend で、この継手は PDF を作りません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "帳票・証憑を PDF で出すために外の変換 backend を使うとき、偽装した入力や pin していない出力 profile で送ってしまうこと、そして戻った PDF が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。出力の bytes の digest を自分で計算して receipt と突き合わせます。",
        "use_when": [
          "帳票・証憑を PDF で出す処理を外の変換 backend に任せ、その手前と後ろの検めがほしいとき。",
          "PDF の出力 profile と params を pin して、決めた形でしか作らせたくないとき。",
          "backend が返した PDF の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
          "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "PDF を作る仕組みそのものがほしいとき。この継手は PDF を作らず、実 backend も含みません。",
          "できた PDF の見た目が元と同じか（忠実さ）や、PDF として壊れていないかを確かめたいとき。どちらも確かめません。",
          "できた PDF を保管したいとき。保存は object store 継手（C-01）の役目です。",
          "電子契約が要るとき。電子契約（J-06）は作っておらず、この部品は J-06 を担いません。電子契約の操作を求める要求は送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "PDF の生成そのものはしません。PDF を作るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "変換エンジンの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-02",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "OCR・テキスト抽出 — 文書・メディア変換継手（Tsugite）",
        "summary": "OCR・テキスト抽出の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へOCRの要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。文字を読み取るのは backend で、この継手は OCR をしません。読めた文字は検証済みの事実とも信頼とも扱わず、一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "画像や紙から文字を取り出して検索・照合の前処理に使うとき、偽装した入力を送ってしまうこと、抽出の結果（extracted_ref）が無いのに成功と扱ってしまうこと、読めた文字を確かめた事実のように扱ってしまうことを防ぎます。",
        "use_when": [
          "画像や PDF の文字の読み取りを外の OCR サービスに任せ、その手前と後ろで検めたいとき。",
          "OCR_EXTRACT の成功には extracted_ref が必ず付いていてほしいとき。付いていない成功は受理しません。",
          "読めた文字を検索・照合の前処理の材料として受け取り、事実として扱うかの判断は核（evidence 系）に任せたいとき。",
          "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "読み取った文字が正しいことを確かめたいとき。OCR で読めた内容の正しさは確かめず、検証済みの事実とも信頼とも扱いません。",
          "OCR エンジンそのものがほしいとき。この継手は OCR をせず、実 backend も含みません。どの OCR サービスにつなぐかは未定です。",
          "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "OCR そのものはしません。文字を読み取るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "OCR で読めた内容の正しさは確かめません（契約の const のとおり、主張しません）。読めた文字を事実として扱うかは evidence 系の核の判断です。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "変換エンジンの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED（OCR では extracted_ref 付き）・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-03",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "形式変換 — 文書・メディア変換継手（Tsugite）",
        "summary": "形式変換の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ形式変換の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。形式を変えるのは backend で、この継手は変換をしません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "互換のために外の変換 backend で文書の形式を変えるとき、偽装した入力や pin していない変換先（出力 profile）で送ってしまうこと、そして戻った bytes が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
        "use_when": [
          "文書の形式変換を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "入力が application/pdf・image/png・image/jpeg・text/plain・application/json のどれかで、先頭の magic bytes で型を確かめてから送りたいとき。",
          "変換先の出力 profile と params を pin したいとき。",
          "変換後の bytes が receipt の digest と一致したときだけ受け取りたいとき。"
        ],
        "do_not_use_when": [
          "docx など、一覧に無い形式を入力として送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません（カタログの一言の「docx→pdf」の docx は、この一覧に入っていません）。",
          "変換の前後で意味や見た目が同じことを確かめたいとき。変換の成功は意味の同一ではなく、見た目の忠実さも確かめません。",
          "変換エンジンそのものがほしいとき。この継手は変換をせず、実 backend も含みません。",
          "変換した結果を保管したいとき。保存は object store 継手（C-01）の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "形式変換そのものはしません。形式を変えるのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "変換エンジンの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-04",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "サムネイル/プレビュー生成 — 文書・メディア変換継手（Tsugite）",
        "summary": "サムネイル/プレビュー生成の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へサムネイル作成の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。サムネイルを作るのは backend で、この継手はサムネイルを作りません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "一覧の見た目に使うサムネイルやプレビューを外の変換 backend で作るとき、偽装した入力や大きすぎる入力を送ってしまうこと、そして戻った画像が要求したものか確かめないまま表示に回してしまうこと（すり替え）を防ぎます。",
        "use_when": [
          "一覧に出すサムネイルやプレビューの作成を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "出力 profile と params を pin して、決めた形でしか作らせたくないとき。",
          "入力の大きさに上限（max_input_bytes）を設け、超えるものは送りたくないとき。",
          "backend が返した画像の bytes が、receipt の digest と一致したときだけ受け取りたいとき。"
        ],
        "do_not_use_when": [
          "サムネイルを作る仕組みそのものがほしいとき。この継手はサムネイルを作らず、実 backend も含みません。",
          "サムネイルの見た目が元と合っているか（忠実さ）を確かめたいとき。確かめません。",
          "作ったサムネイルを保管したいとき。保存は object store 継手（C-01）の役目です。",
          "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "サムネイル・プレビューの作成そのものはしません。作るのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "変換エンジンの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-05",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "画像最適化 — 文書・メディア変換継手（Tsugite）",
        "summary": "画像最適化の裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ画像最適化の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。画像を縮小・軽量化するのは backend で、この継手は画像を変えません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "表示速度のために外の変換 backend で画像を縮小・軽量化するとき、偽装した入力や pin していない設定で送ってしまうこと、そして戻った画像が要求したものか確かめないまま使ってしまうこと（すり替え）を防ぎます。",
        "use_when": [
          "画像の縮小・軽量化を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "入力が image/png・image/jpeg などの許可した型で、先頭の magic bytes で型を確かめてから送りたいとき。",
          "最適化の出力 profile と params を pin したいとき。",
          "backend が返した画像の bytes が、receipt の digest と一致したときだけ受け取りたいとき。"
        ],
        "do_not_use_when": [
          "画像を縮小・軽量化する仕組みそのものがほしいとき。この継手は画像を変えず、実 backend も含みません。",
          "最適化した画像の見た目が元と合っているか（忠実さ）を確かめたいとき。確かめません。",
          "最適化した画像を保管したいとき。保存は object store 継手（C-01）の役目です。",
          "許可リストに無い形式の入力を送りたいとき。magic bytes で確かめられる型は application/pdf・image/png・image/jpeg・text/plain・application/json だけで、一覧に無い型は送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "画像最適化そのものはしません。縮小・軽量化するのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "変換の見た目の忠実さは確かめません。変換の成功は意味の同一ではありません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "変換エンジンの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-06",
        "category": "J/文書・メディア処理",
        "kind": "Component",
        "title": "電子契約（envelope） — 電子契約（envelope）の部品（Component）",
        "summary": "電子契約（envelope）の部品（Component）は、署名フローを束ねる部品です。文書の digest を固定し（B-07）、決めた署名者それぞれの署名を pin した鍵で検め（B-02）、状態の遷移（開く・署名・完了・取り消し・時刻）を監査台帳の継手（H-01）に記録できたときだけ状態を進めます。完了の digest に付けた時刻トークンの検め（B-06）は任意で束ねます。台帳の結果が分からないときは状態を進めず保留にします（fail-closed）。署名が揃ったことは、法的な受け入れ・権限・承認ではありません。",
        "problem": "B 署名と別の層として、署名フローで、台帳に記録の無い状態を名乗ること、ほかの envelope・文書・署名者のための署名を流用されること、台帳の結果が分からないまま状態を進めてしまうこと、文書の本文を台帳へ渡してしまうことを防ぎます。",
        "use_when": [
          "1 つの文書に、決めた署名者（1〜20 人）の署名を集め、全員の署名が検めを通って記録されたときだけ COMPLETED にしたいとき。",
          "署名者ごとの署名を、使う側が固定した公開鍵（pinned_keys）で B-02 の部品に検めさせたいとき。署名者は signingBytes(signer_ref) が返す bytes（envelope_ref・文書の digest・signer_ref を含む）に署名します。",
          "状態の遷移を、H-01 の監査台帳の継手に AuditEventEnvelope として追記し、記録できた遷移（APPENDED_VERIFIED か ALREADY_APPENDED）だけを状態にしたいとき。台帳へ渡すのは遷移の payload の digest だけです。",
          "完了の後に、完了の digest に付けた時刻トークンを、B-06 の verifyToken を束ねた関数で 1 回だけ検めて記録したいとき（任意）。",
          "台帳の結果が分からなかった記録を、同じ封筒で送り直したいとき（resolvePending）。"
        ],
        "do_not_use_when": [
          "署名が揃ったこと・時刻が付いたことを、法的な受け入れ・権限・委任・承認として使いたいとき。「署名済み」は、pin した鍵の持ち主がこの envelope・この文書・この署名者の bytes に署名したことだけを示します。",
          "署名者の本人確認が要るとき。この部品は本人確認・権限・委任、署名の法的な効力を扱いません。",
          "署名の依頼の送付（通知）まで任せたいとき。通知は束ねません。送るなら呼び出し側が D-01 の継手で送ります。",
          "時刻トークンの取得（TSA への依頼）まで任せたいとき。取得は含まず、B-06 も検めだけです。",
          "外部の電子署名サービスへつなぎたいとき。その場合は別の継手の契約が要り、今は作りません。",
          "再起動をまたいでフローを続けたいとき。状態はこのプロセスの中だけで、台帳の記録から状態を組み立て直す機能は v1 にありません。",
          "同じ envelope_ref で 2 つのフローを作りたいとき。event_id が重なり、台帳が断ります（v1 は envelope_ref ごとに 1 つのフロー）。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違うとき（参照が不正、署名者や鍵の重複、pin に無い鍵、21 人以上、台帳の設定の余分なキー、getter）、INVALID_OPTIONS で止めます。",
          "その状態ではできない操作のとき（開く前・取り消しの後の署名、完了の後の取り消し、完了の前・2 回目の時刻）WRONG_STATE、前の操作が終わっていないとき BUSY で止めます。",
          "決めた署名者でないとき UNKNOWN_SIGNER、すでに署名したとき ALREADY_SIGNED、署名が B-02 の検めを通らないとき（ほかの envelope・文書・署名者の bytes、ほかの鍵）SIGNATURE_REJECTED で止めます。",
          "台帳の結果が分からないとき（UNKNOWN・例外・返事が無い）、状態を進めずに LEDGER_UNKNOWN で保留にし、保留の間は resolvePending のほかを RECORD_PENDING で止めます。保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。",
          "台帳が断ったとき（REFUSED・RECEIPT_REJECTED・HALTED など）や台帳の先頭の形が不正なとき、状態を進めず保留も残さずに LEDGER_REFUSED で止めます（先頭が不正なら送りません）。",
          "時刻の依頼が完了の digest でない・hash_algo が \"SHA256\" でない・B-06 の検めを通らないとき TIMESTAMP_REJECTED、verifyTimestamp の port が無いとき NOT_CONFIGURED で止めます。",
          "台帳の state() や検めの port が例外を投げたとき PORT_FAILED、port の結果の getter が投げたとき INSPECTION_FAILED、contentAddress の結果が不正なとき DIGEST_FAILED、timestamp が不正なとき CLOCK_FAILED で止めます。"
        ],
        "what_it_does_not_do": [
          "電子契約（envelope）の部品は継手ではありません。自分の Request/Receipt は無く、台帳への記録という外への効果は、束ねた H-01 の継手が自分の契約（audit-ledger/v1）で起こします。この部品自身は外へ触れません。",
          "束ねた部品の意味（digest の同一性・pin した鍵での検め・台帳の受領の照合・時刻トークンの検め、どれも承認ではない）は、それぞれの部品の側に残ります。",
          "署名が揃ったこと・時刻が付いたことは、法的な受け入れ・権限・委任・承認ではありません（approval: false・authority: NOT_DERIVED、derives_authority: false）。",
          "署名者の本人確認・権限・委任、署名の法的な効力は扱いません。",
          "通知（署名の依頼の送付）と、時刻トークンの取得（TSA への依頼）は含みません。",
          "本物の台帳・TSA には一度も接続していません。単体試験とファズは代役で、署名の検証も代役です（暗号の正しさは B-02 の役目）。本物の 4 部品のコードとの結線は、手元の代役の backend を相手に確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の電子署名サービスの使い方の雛形ではありません。"
        ],
        "integration": "src/envelope.js の createEnvelopeFlow(options) で作ります。受け取るのは envelope_ref・signers・pinned_keys・ledger・ports などで、{ ok, errors, instance } を返します。各操作は { ok, errors, approval: false, phase, signed, … } を返します。利用側が用意する port は contentAddress（B-07）・verifySignature（B-02）・ledger.append と state（H-01 の継手のクライアント）・任意の verifyTimestamp（B-06）です。副作用は束ねた継手が起こし（EXTERNAL_IRREVERSIBLE）、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-07",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "バーコード/QR 生成・読取 — 文書・メディア変換継手（Tsugite）",
        "summary": "バーコード/QR 生成・読取の項目のうち、文書・メディア変換継手（Tsugite）が扱うのは QR の読取（QR_DECODE）の要求だけです。外の変換 backend へ画像を渡す前に入力と操作を検め、戻った receipt と出力を照合する継手（つぎて）です。QR を読むのは backend で、この継手は読みません。QR の生成は契約に無く、読めた内容を検証済みの事実とも信頼とも扱いません（fail-closed）。",
        "problem": "現場の入出力で QR を読むとき、偽装した画像を送ってしまうこと、抽出の結果（extracted_ref）が無いのに成功と扱ってしまうこと、読めた内容を確かめた事実のように扱ってしまうことを防ぎます。",
        "use_when": [
          "QR の読取を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "QR_DECODE の成功には extracted_ref が必ず付いていてほしいとき。付いていない成功は受理しません。",
          "読めた内容を受け取り、事実として扱うかの判断は核（evidence 系）に任せたいとき。",
          "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "QR を生成したいとき。QR の生成は契約に無く、この継手が扱うのは読取（QR_DECODE）だけです。",
          "バーコードを扱いたいとき。この部品の操作に挙がっているのは QR 読取で、バーコードは挙がっていません。",
          "読めた内容が正しいことを確かめたいとき。QR で読めた内容の正しさは確かめず、検証済みの事実とも信頼とも扱いません。",
          "QR を読む仕組みそのものがほしいとき。この継手は読取をせず、実 backend も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "QR の生成はしません。契約に無く、扱うのは QR_DECODE の読取の要求だけです。",
          "QR の読取そのものもしません。読むのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "QR で読めた内容の正しさは確かめません（契約の const のとおり、主張しません）。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED（QR では extracted_ref 付き）・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-08",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "透かし・スタンプ — 文書・メディア変換継手（Tsugite）",
        "summary": "透かし・スタンプの裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ透かしの要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。透かしを入れるのは backend で、この継手は透かしを入れません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "出所・秘匿の刻印として外の変換 backend で透かしを入れるとき、偽装した入力や pin していない設定で送ってしまうこと、そして戻った出力が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
        "use_when": [
          "文書や画像への透かしを外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "透かしの出力 profile と params を pin して、決めた形でしか入れさせたくないとき。",
          "backend が返した出力の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
          "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "電子契約が要るとき。電子契約（J-06）は作っておらず、この部品は J-06 を担いません。電子契約の操作を求める要求は送りません。",
          "何を刻印するか・何を秘匿するかを決めたいとき。決定は核（redact-facts）と人の役目で、この継手は送る前と受け取った後の検めだけです。",
          "透かしの入った出力を、信頼や開示の許可の根拠にしたいとき。どの結果も trust: NOT_DERIVED で、開示の許可も導きません。",
          "透かしを入れる仕組みそのものがほしいとき。この継手は透かしを入れず、実 backend も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "透かしを入れることそのものはしません。入れるのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "電子契約はしません。電子契約（J-06）は作っていません。",
          "変換の見た目の忠実さ（透かしが見たとおりに入っているか）は確かめません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "J-09",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "秘匿レンダリング — 文書・メディア変換継手（Tsugite）",
        "summary": "秘匿レンダリングの裏方として、文書・メディア変換継手（Tsugite）は、外の変換 backend へ黒塗りの描画の要求を渡す前に入力・操作・出力 profile を検め、戻った receipt と出力の bytes を照合する継手（つぎて）です。黒塗りを描くのは backend で、何を黒塗りにするかを決めるのは核（redact-facts）と人です。この継手は描画の要求を渡して照合するだけで、開示を許可しません（fail-closed）。",
        "problem": "核（redact-facts）が決めた黒塗りを外の変換 backend で描画させるとき、偽装した入力を送ってしまうこと、「開示の許可あり」と名乗る receipt を受け取ってしまうこと、戻った出力が要求したものか確かめないまま渡してしまうこと（すり替え）を防ぎます。",
        "use_when": [
          "核（redact-facts）と人が決めた黒塗りの描画を外の変換 backend に任せ、その手前と後ろで検めたいとき。",
          "描画の結果を開示の許可と取り違えたくないとき。disclosure_authorization が NOT_DERIVED 以外の receipt は受理しません。",
          "backend が返した出力の bytes が、receipt の digest と一致したときだけ受け取りたいとき。",
          "同じ要求を二重に送りたくないとき。障害のあとの再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "何を黒塗りにするかを決めたいとき。黒塗りの対象の決定は核と人の役目で、この継手は描画の要求を渡すだけです。",
          "黒塗りした出力を開示してよいかを判断したいとき。黒塗りの描画は開示の許可ではありません。",
          "黒塗りが見たとおりに描かれているか（忠実さ）を確かめたいとき。確かめません。",
          "黒塗りを描く仕組みそのものがほしいとき。この継手は描画をせず、実 backend も含みません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "receipt が検証済み・開示の許可あり・信頼できると名乗るもの（content_verification NOT_VERIFIED・fidelity_assertion NOT_ASSERTED・disclosure_authorization NOT_DERIVED・trust_assertion NOT_DERIVED 以外の値）は受理しません。",
          "入力を検めます。入力の bytes の digest が要求と合わないもの、上限（max_input_bytes）を超えるもの、許可していない content-type のもの、先頭の magic bytes が content-type と合わないもの（偽装した入力）は、変換 backend へ送りません。",
          "操作・出力 profile・params を検めます。許可していない操作、電子契約（J-06）の操作、pin していない出力 profile・params を求める要求は送りません。",
          "出力の bytes の digest を自分で計算し、receipt と違えば受理せず（OUTPUT_REJECTED）、以後は停止します（すり替えた出力は受け取りません）。",
          "receipt（MediaTransformReceipt）を照合します。request_digest の偽り、別の要求の receipt、操作のすり替え、外への効果を増やす receipt は受理せず、以後は停止します（HALTED）。",
          "結果の形を検めます。出力の digest の無い SUCCEEDED、extracted_ref の無い OCR_EXTRACT・QR_DECODE の SUCCEEDED、出力や効果を伴う FAILED は受理しません。PARTIAL は成功として扱いません。",
          "同じ request_id は二度送りません。backend の障害は UNKNOWN とし、再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "黒塗りの描画そのものはしません。描くのは外の変換 backend で、この継手がするのは送る前の検めと、受け取った後の照合だけです。",
          "黒塗りの対象を決めず、開示を許可しません。対象の決定は核（redact-facts）と人の役目です。",
          "変換の見た目の忠実さは確かめません。",
          "実 backend を含みません。どの変換エンジン・OCR サービスにつなぐか、port の実装、pin する profile は未定で、接続するときに別の関門（gate）で決めます。",
          "magic bytes は先頭の数 bytes だけを見ます。ファイル全体の構造の検査（壊れた PDF など）はしません。",
          "どの結果も承認ではありません（approval: false）。どの結果にも content_verification: NOT_VERIFIED・trust: NOT_DERIVED が付きます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/document-media/v1 でつなぎます。createMediaTransformClient({ cfg, port }) の transform(request, bytes) は、TRANSFORMED・PARTIAL・FAILED・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・OUTPUT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: 変換 backend への port（transform、任意で lookup）と cfg（許可リスト・pin・上限）。副作用は NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-01",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "構造化ログ — 可観測性 sink 継手（Tsugite）",
        "summary": "可観測性 sink 継手（Tsugite）は、機械可読の構造化ログ（ts・level・event・attrs の記録）の batch を、許可したイベントとキー・型・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。受け付けられなかった記録は欠落として返します。",
        "problem": "障害調査のためにログを外部の sink へ流すとき、メールアドレスや資格情報のような機微な値や自由文がログに混じって sink へ複製されること、そして sink が受け付けなかった記録が黙って消えることを防ぎます。許可したキーの短い基本型だけを送り、欠落は SUNK_PARTIAL と dropped で返します。",
        "use_when": [
          "機械可読の記録として、ts・level・event・attrs の形の構造化ログを外部の sink へ送りたいとき。",
          "ログに載せてよいイベント（log_events）とキー（log_attrs）を決めておき、それ以外を送らないようにしたいとき。",
          "ログから機微な値を sink へ複製したくないとき。最小化の記録（redaction_manifest_ref）を必須にできます。",
          "sink が受け付けなかったログを、黙って捨てずに欠落の事実として受け取りたいとき。",
          "同じ batch を二重に流したくないとき。同じ request_id は ALREADY_SUNK として送りません。"
        ],
        "do_not_use_when": [
          "自由文のメッセージや長い本文をそのままログに残したいとき。自由文の欄はなく、許可キーの短い基本型だけを送ります。",
          "エラー追跡（K-04）やアラート（K-06）が要るとき。別の部品で、この継手には含みません。",
          "すぐに使える sink がほしいとき。実 sink（各社の log の受け口）は含まず、まだ決まっていません。",
          "sink の受付の数を、ログの内容が正しいことの検証として扱いたいとき。観測は検証済みの事実ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "機微らしい値を検めます。メールアドレス、10 桁以上の数字の並び、Bearer、JWT、鍵、資格情報らしい記述が含まれていれば送りません（REFUSED）。",
          "自由文の欄、許可していないイベントやキー（attrs）を含む記録は送りません。",
          "大きさを検めます。batch の大きさ（max_payload_bytes）、記録の数（max_records）、値の長さ（max_attr_len）の上限を超えるものは送りません。",
          "pin した sink を検めます。pin していない sink へは送りません。payload の改ざん（payload_digest の不一致）があれば送りません。",
          "最小化の記録（redaction_manifest_ref）を必須とし、無いものは送りません。これは契約より厳しくした点です。",
          "受領（SinkReceipt）を照合します。request の digest を偽った受領や、送った数より多い受付は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。受領の observation_state が UNKNOWN なら照合しません。"
        ],
        "what_it_does_not_do": [
          "ログの構造を決めるのは核（policy-facts）、最小化を決めるのは核（redact-facts）の役目です。この継手がするのは sink の手前での検めだけです。",
          "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可キーと値の短さで抑えているだけで、見分けてはいません。",
          "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
          "この部品は K-01 のほか、K-02（メトリクス）・K-03（分散トレース）も同じ 1 つの部品として担い、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "監視サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に log の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・許可イベントとキー・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-02",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "メトリクス — 可観測性 sink 継手（Tsugite）",
        "summary": "可観測性 sink 継手（Tsugite）は、数値の常時計測であるメトリクス（ts・name・type・value・labels の記録）の batch を、許可した metric とラベル・型・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）の受付の数を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。受け付けられなかった記録は欠落として返します。",
        "problem": "メトリクスを外部の sink へ流し続けるとき、決めていない metric やラベルが増えたり、ラベルに機微な値が混じって sink へ複製されたりすること、そして sink が受け付けなかった数値が黙って消えることを防ぎます。送った数より多い受付のような受領の異常では止まります。",
        "use_when": [
          "数値の常時計測として、ts・name・type・value・labels の形のメトリクスを外部の sink へ送りたいとき。",
          "送ってよい metric（metrics）とラベル（metric_labels）を決めておき、それ以外を送らないようにしたいとき。",
          "sink が受け付けなかった数値を、黙って捨てずに欠落の事実（SUNK_PARTIAL と dropped）として受け取りたいとき。",
          "送った数より多い受付を返す受領を異常として扱い、以後の送信を止めたいとき。",
          "同じ batch を二重に流したくないとき。同じ request_id は ALREADY_SUNK として送りません。"
        ],
        "do_not_use_when": [
          "メトリクスの閾値を判定したり、アラートを出したりしたいとき。閾値の事実は核（monitor-detect）の役目で、アラート（K-06）は別の部品です。",
          "ラベルに自由文や長い値を載せたいとき。自由文の欄はなく、許可ラベルの短い基本型だけを送ります。",
          "すぐに使える sink がほしいとき。実 sink（各社の metrics の受け口）は含まず、まだ決まっていません。",
          "sink の受付の数を、計測値が正しいことの検証として扱いたいとき。観測は検証済みの事実ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "許可していない metric やラベル、自由文の欄を含む記録は送りません（REFUSED）。",
          "機微らしい値を検めます。メールアドレス、10 桁以上の数字の並び、Bearer、JWT、鍵、資格情報らしい記述が含まれていれば送りません。",
          "大きさを検めます。batch の大きさ（max_payload_bytes）、記録の数（max_records）、値の長さ（max_attr_len）の上限を超えるものは送りません。",
          "pin した sink を検めます。pin していない sink へは送りません。payload の改ざん（payload_digest の不一致）があれば送りません。",
          "受領（SinkReceipt）の受付の数を照合します。送った数より多い受付や、request の digest を偽った受領は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "最小化の記録（redaction_manifest_ref）を必須とし、無いものは送りません。これは契約より厳しくした点です。",
          "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。受領の observation_state が UNKNOWN なら照合しません。"
        ],
        "what_it_does_not_do": [
          "閾値の判定はしません。metrics の閾値の事実は核（monitor-detect）、最小化の決定は核（redact-facts）の役目で、この継手は sink の手前で検めるだけです。",
          "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可ラベルと値の短さで抑えているだけで、見分けてはいません。",
          "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
          "K-02 専用の部品ではありません。K-01（構造化ログ）・K-03（分散トレース）と同じ 1 つの部品で、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "監視サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に metrics の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・許可 metric とラベル・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-03",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "分散トレース — 可観測性 sink 継手（Tsugite）",
        "summary": "可観測性 sink 継手（Tsugite）は、処理の経路追跡に使う trace（trace_id・span_id・parent_span_id・name・start_ts・end_ts・attrs の span）の batch を、span の相関（同じ trace・根の span が 1 つ・親が同じ batch の中）、許可キー・機微らしい値の不在・最小化の記録・digest を検めてから sink へ送り、返る受領（SinkReceipt）を照合する継手（つぎて）です。一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "遅延の原因を追うために trace を外部の sink へ流すとき、別の trace の span が混じる、親の見つからない span がある、根が複数ある、といった相関の壊れた trace や、途中の断片だけの trace を流してしまうことを防ぎます。span の attrs に機微な値が混じって sink へ複製されることも拒否します。",
        "use_when": [
          "処理の経路追跡として、trace_id・span_id・parent_span_id・name・start_ts・end_ts・attrs の形の span を外部の sink へ送りたいとき。",
          "1 つの trace をまとまった形（根の span がちょうど 1 つで、親がすべて同じ batch の中にある形）でだけ送りたいとき。",
          "trace ごとに相関の参照（correlation_ref）を必ず付けて送りたいとき。trace では correlation_ref を必須としています。",
          "span に載せてよいキー（span_attrs）を決めておき、それ以外を送らないようにしたいとき。",
          "sink が受け付けなかった span を、黙って捨てずに欠落の事実（SUNK_PARTIAL と dropped）として受け取りたいとき。"
        ],
        "do_not_use_when": [
          "trace の途中の断片だけを送りたいとき。根の span がちょうど 1 つで、親がすべて同じ batch の中にある batch だけを送ります。",
          "trace の連結が正しいかの最終の判断を、この継手に任せたいとき。trace の連結は核（provenance-facts）の役目です。",
          "エラー追跡（K-04）やアラート（K-06）が要るとき。別の部品で、この継手には含みません。",
          "すぐに使える sink がほしいとき。実 sink（各社の trace の受け口）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "trace の相関を検めます。別の trace の span が混じる、終了が開始より前の span があるときは送りません（REFUSED）。",
          "根の span の数と親の場所を検めます。根が複数ある batch、親が batch の外にある span（orphan）を含む batch は送りません。",
          "trace に相関の参照（correlation_ref）と最小化の記録（redaction_manifest_ref）が無いものは送りません。どちらも契約より厳しくした点です。",
          "span の attrs を検めます。許可していないキー、自由文の欄、メールアドレス・10 桁以上の数字の並び・Bearer・JWT・鍵・資格情報らしい記述を含むものは送りません。",
          "大きさと宛先を検めます。batch の大きさ・記録の数・値の長さの上限を超えるもの、pin していない sink 宛て、payload の改ざん（payload_digest の不一致）があるものは送りません。",
          "受領（SinkReceipt）を照合します。request の digest を偽った受領や、送った数より多い受付は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "sink の障害（500 の応答）は UNKNOWN とし、再送は照会（lookup）で解きます。同じ request_id は二重に流しません（ALREADY_SUNK）。"
        ],
        "what_it_does_not_do": [
          "trace の連結を決めるのは核（provenance-facts）、最小化を決めるのは核（redact-facts）の役目です。この継手がするのは sink の手前での検めだけです。",
          "機微の判定は文字の型による近似です。名前・住所などの自由な語は、許可キーと値の短さで抑えているだけで、見分けてはいません。",
          "実 sink を含みません。どの受け口につなぐか、port の実装、pin する対象は未定で、接続するときに別の関門で決めます。",
          "観測は検証済みの事実ではありません。verified が true になるのは、受領が VERIFIED と検証者を示したときだけです。成功は承認ではありません（approval: false）。",
          "K-03 専用の部品ではありません。K-01（構造化ログ）・K-02（メトリクス）と同じ 1 つの部品で、試験と検査も共通です。ID ごとの行（K-01・K-02・K-03）が、この部品を指します。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。payload の中身の形は、契約ではなく継手の側の定めです。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "監視サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/request-receipt-group/v1（SinkRequest / SinkReceipt）でつなぎます。createSinkClient({ cfg, port }) の sink(SinkRequest, bytes) に span の { records: [...] } を渡すと SUNK・SUNK_PARTIAL・ALREADY_SUNK・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。cfg は pin する sink・span の許可キー・上限。利用側は sink への port（write、任意で lookup）を用意します。副作用は EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-04",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "エラー追跡 — 可観測の拡張継手（Tsugite）",
        "summary": "可観測の拡張継手（Tsugite）は、K-04 エラー追跡としては、エラー追跡の要求（ErrorTrackRequest）を、pin した送り先と、解決できる失敗の参照（failure_ref）で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "エラーの記録を外部のエラー追跡の基盤へ送るとき、決めていない送り先へ送ってしまうこと、解決できない失敗の参照を送ってしまうこと、そして基盤の receipt を照合しないまま「記録できた」と扱ってしまうことを防ぎます。記録できたことを、検証済みの事実として扱わないための検めです。",
        "use_when": [
          "「たまに落ちる」のような失敗の記録を、エラー追跡の基盤へ送りたいとき。",
          "送ってよいエラー追跡の送り先（sink_refs）を決めておき、それ以外へ送らないようにしたいとき。",
          "失敗の参照（failure_ref）を、ai-execution-error の記録へ解決できるときだけ送りたいとき。",
          "同じエラーの記録を二重に送りたくないとき。同じ request_id は二度送らず、再送は照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "例外そのものを集めたいとき。この継手はエラー追跡の要求を運ぶ継手で、例外を集める機能は含みません。",
          "記録できたことを、検証済みの事実や承認として扱いたいとき。DONE の結果は verified_fact: NOT_ASSERTED で、approval: false です。",
          "failure_ref が指す記録の中身まで検めたいとき。この継手が確かめるのは、解決できるかどうかだけです。",
          "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していない送り先への要求は送りません。",
          "failure_ref を載せた要求は、port の resolveFailureRef で ai-execution-error の記録へ解決できないとき、または解決の手段が無いとき、送りません（REFUSED）。",
          "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
          "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
          "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "例外を集める機能は含みません。エラー追跡の要求を検めて基盤へ運び、receipt を照合するだけです。",
          "failure_ref が指す ai-execution-error の記録の中身は検めません（解決できるかだけ）。",
          "記録できたことを、検証済みの事実とはしません（verified_fact: NOT_ASSERTED）。",
          "記録できたことは承認ではありません（approval: false）。",
          "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: track_state・observation_state・verified_fact: NOT_ASSERTED。利用側の用意: cfg（pin する sink_refs ほか）と port（submit・任意で lookup・failure_ref 用に resolveFailureRef）。副作用は最大 EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-05",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "ヘルスチェック — 可観測の拡張継手（Tsugite）",
        "summary": "可観測の拡張継手（Tsugite）は、K-05 ヘルスチェックとしては、ヘルスチェックの要求（HealthCheckRequest）を pin した対象で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。ヘルスの PASS を意味の正しさと取り違えず、一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "生死・準備状態を確かめるとき、決めていない対象へ問い合わせてしまうこと、そしてヘルスの PASS を「中身も正しい」と読み違えることを防ぎます。意味の正しさを名乗る receipt は受理しません。",
        "use_when": [
          "生死・準備状態のヘルスチェックの要求を、基盤へ運びたいとき。",
          "問い合わせてよい対象（health_targets）を決めておき、それ以外を問い合わせないようにしたいとき。",
          "ヘルスの結果（health_state）と、意味の正しさ（semantic_correctness）を分けて受け取りたいとき。"
        ],
        "do_not_use_when": [
          "ヘルスの PASS を、処理結果の意味の正しさの証明として使いたいとき。この継手は PASS から意味の正しさを導きません。",
          "異常を見つけて自動で復旧させたいとき。この継手はヘルスチェックの要求を運ぶ継手で、復旧の機能は含みません。",
          "閾値で異常を判定したいとき。閾値と検出の事実は核（monitor-detect）の役目です。",
          "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していない対象へのヘルスチェックの要求は送りません。",
          "意味の正しさを主張するヘルスの receipt は受理しません。",
          "ヘルスチェックの効果は NONE です。外への効果を名乗る receipt は、効果を増やすものとして受理しません。",
          "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
          "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
          "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "ヘルスの PASS から、意味の正しさを導きません。",
          "復旧の機能は含みません。ヘルスチェックの要求を検めて基盤へ運び、receipt を照合するだけです。",
          "ヘルスの結果は承認ではありません（approval: false）。",
          "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: health_state・semantic_correctness。利用側の用意: cfg（health_targets などの pin する一覧）と port（submit、任意の lookup）。ヘルスチェックの副作用は NONE です。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-06",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "アラート・オンコール — 可観測の拡張継手（Tsugite）",
        "summary": "可観測の拡張継手（Tsugite）は、K-06 アラート・オンコールとしては、アラートの要求（AlertRequest）を pin したアラートと宛先で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。届けたアラートと受け取られたアラートを分けて返し、一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "異常を通報するとき、決めていない宛先や任意の URL へアラートを送ってしまうこと、そして「届けた」を「受け取られた」と読み違えることを防ぎます。",
        "use_when": [
          "異常の通報（アラート）の要求を、基盤へ運びたいとき。",
          "アラート（alert_refs）と宛先（alert_destinations）を pin し、任意の URL へ送らないようにしたいとき。",
          "届けた（delivered）と受け取られた（acknowledged）を、分けて受け取りたいとき。"
        ],
        "do_not_use_when": [
          "アラートが実際に人に届いたか・対応されたかを確かめたいとき。この継手は receipt の状態を返すだけで、確かめません。",
          "オンコールの当番を組んだり、呼び出しを段階的に広げたりしたいとき。この継手はアラートの要求を運ぶ継手で、それらの機能は含みません。",
          "異常を閾値で判定したいとき。閾値と検出の事実は核（monitor-detect）、アラートの方針は核（policy-facts）の役目です。",
          "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していないアラート、pin していない宛先への要求は送りません。宛先に任意の URL を差し込む要求（destination injection）も送りません。",
          "delivered は DELIVERED か ACKNOWLEDGED のときだけ、acknowledged は ACKNOWLEDGED のときだけ認めます。外への効果を伴わずに届けたと名乗る receipt は受理しません。",
          "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
          "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
          "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "アラートが実際に人に届いたか・対応されたかは確かめません。receipt の状態を返すだけです。",
          "オンコールの当番や呼び出しの機能は含みません。アラートの要求を検めて基盤へ運び、receipt を照合するだけです。",
          "アラートを届けたことは承認ではありません（approval: false）。",
          "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: alert_state・delivered・acknowledged。利用側の用意: pin する一覧（alert_refs・alert_destinations など）の cfg と、port（submit・任意の lookup）。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-07",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "フィーチャーフラグ — 可観測の拡張継手（Tsugite）",
        "summary": "可観測の拡張継手（Tsugite）は、K-07 フィーチャーフラグとしては、機能フラグの要求（FeatureFlagRequest、READ・SET・CLEAR）を pin したフラグと値で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。フラグの状態から許可を導かず、一つでも合わなければ送らず、受理しません（fail-closed）。",
        "problem": "機能の出し分けのために機能フラグを読み書きするとき、決めていないフラグや値を立ててしまうこと、そしてフラグが立っていることを「許可された」と読み違えることを防ぎます。許可を名乗るフラグの receipt は受理しません。",
        "use_when": [
          "機能フラグの READ・SET・CLEAR の要求を、基盤へ運びたいとき。",
          "扱ってよいフラグ（flag_refs）と値（flag_value_refs）を決めておき、それ以外を扱わないようにしたいとき。",
          "フラグの状態（flag_state）と許可（authorization）を分けて受け取りたいとき。"
        ],
        "do_not_use_when": [
          "フラグの状態を、利用者や機能の許可として使いたいとき。この継手はフラグの状態から許可を導きません。",
          "READ でフラグの値そのものを受け取りたいとき。値は receipt に無く、この継手は状態だけを返します。",
          "段階公開の割合や対象を決めたいとき。この継手は機能フラグの要求を運ぶ継手で、出し分けを決める機能は含みません。フラグの方針は核（policy-facts）の役目です。",
          "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していないフラグ・pin していない値の要求は送りません。",
          "value_ref は SET のときだけ必須で、READ に値を載せた要求は送りません。op と効果が合わない要求（READ は NONE、SET・CLEAR は EXTERNAL_REVERSIBLE）も送りません。",
          "状態が op に対応しない receipt（READ→READ・SET→SET・CLEAR→CLEARED か FAILED 以外）は受理しません。",
          "許可を名乗るフラグの receipt は受理しません。",
          "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
          "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
          "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "フラグの状態から許可を導きません。",
          "フラグの値そのもの（READ で何が返るか）は返しません。状態だけを返します。",
          "出し分けを決める機能は含みません。機能フラグの要求を検めて基盤へ運び、receipt を照合するだけです。",
          "フラグを立てたことは承認ではありません（approval: false）。",
          "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: flag_state・authorization。利用側の用意: pin する一覧（flag_refs・flag_value_refs など）の cfg と、port（submit・任意の lookup）。副作用は READ が NONE、SET・CLEAR が EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "K-08",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "使用量メータ — 可観測の拡張継手（Tsugite）",
        "summary": "可観測の拡張継手（Tsugite）は、K-08 使用量メータとしては、使用量のイベントを記録する要求（UsageMeterRequest）を、pin した計測器と単位、量の上限で検めてから基盤へ運び、戻った receipt を契約どおりに照合する継手（つぎて）です。課金はせず、量の正しさも確かめません。一つでも合わなければ、送らず、受理しません（fail-closed）。",
        "problem": "使用量を外部の計測の基盤へ記録するとき、決めていない計測器や単位で記録してしまうこと、小数・負・上限を超える量を送ってしまうこと、そして記録した量を検証済みの業務の事実と読み違えることを防ぎます。",
        "use_when": [
          "使用量のイベントを、計測の基盤へ運びたいとき。",
          "記録してよい計測器（meter_refs）と単位（unit_refs）、量の上限（max_quantity）を決めておき、それ以外を送らないようにしたいとき。",
          "同じ使用量を二重に記録したくないとき。同じ冪等キーで別の量は送らず、同じ request_id は二度送りません。"
        ],
        "do_not_use_when": [
          "課金をしたいとき。この継手は使用量のイベントを運ぶ継手で、課金の機能は含みません。",
          "記録した量を、そのまま課金に使いたいとき。量の正しさ（課金に使えるか）は確かめず、検証済みの業務の事実ではありません。",
          "小数の量を記録したいとき。量は 0 以上の整数だけで、小数の量は digest を作れないため送りません。",
          "すぐに使える基盤がほしいとき。実の基盤（どのエラー追跡・監視・通知・フラグ・計測のサービスか）は含まず、まだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "pin していない計測器・pin していない単位の要求は送りません。",
          "量が小数のとき、負のとき、上限（max_quantity）を超えるときは送りません。max_quantity は使用量を記録するとき必須です。",
          "量を検証済みの業務の事実と主張する receipt は受理しません。",
          "参照が空のとき、パスや「..」を含むとき（path traversal）、追加のフィールドがあるときは送りません。参照は pin した一覧にあるものだけで、空の一覧は何も許しません。",
          "同じ request_id は二度送りません（ALREADY_DONE）。同じ冪等キーで別の中身の要求は送らず（REFUSED）、重複（DEDUP_HIT）の receipt が新しい効果を名乗れば受理しません。",
          "receipt を照合します。request_digest の偽り、外への効果を増やす receipt、効果の分からない（UNKNOWN の）receipt、別の要求の receipt は受理せず（RECEIPT_REJECTED）、以後は停止します（HALTED）。",
          "基盤の障害は UNKNOWN として返します。再送は照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "課金はしません。使用量のイベントを検めて基盤へ運び、receipt を照合するだけです。",
          "量の正しさ（課金に使えるか）は確かめません。記録した量は検証済みの業務の事実ではありません。",
          "小数の量は扱いません（契約は number ですが、この継手は 0 以上の整数に締めています）。",
          "使用量を記録できたことは承認ではありません（approval: false）。",
          "許可・意味の正しさ・業務の事実の判断は、核（monitor-detect・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "実の基盤を含みません。どのサービスにつなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門（gate）で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/observability-ext/v1。createObservabilityExtClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（すべて approval: false）。DONE の result: meter_state・business_fact_verification。利用側の用意: pin する一覧（meter_refs・unit_refs など）と max_quantity の cfg、port（submit・任意の lookup）。副作用は EXTERNAL_REVERSIBLE（DEDUP_HIT・FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-01",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "タイムゾーン変換 — タイムゾーン変換の部品（Primitive）",
        "summary": "タイムゾーン変換の部品（Primitive）は、epoch ミリ秒（安全な整数）と IANA の zone の壁時計（年月日・時分秒・ミリ秒）を行き来する単独の部品です。zone の規則は Node.js の組み込みの Intl（ICU の tz データ）に従い、結果にはいつも tz データと ICU の版を添えます。存在しない壁時計・二度ある壁時計を黙って直さず、{ ok, errors } を返して例外を投げません（fail-closed）。",
        "problem": "「時間がずれる」の裏で、夏時間の飛びで存在しない時刻や、重なりで二度ある時刻を、ほかの時刻に黙って寄せてしまうこと、2 月 30 日・24 時・60 秒のような欄を繰り上げて受け取ってしまうことを防ぎます。結果に tz データと ICU の版を添えるので、どの規則で出した答えかを後から確かめられます。",
        "use_when": [
          "epoch ミリ秒の瞬間を、IANA の zone（例: Asia/Tokyo）の壁時計と UTC からのずれに変えたいとき（toZoned）。",
          "zone の壁時計を epoch ミリ秒の瞬間に変えたいとき（fromZoned）。二度ある壁時計は既定で止め、earlier・later を明示したときだけ早い方・遅い方を選びます。",
          "二度ある壁時計を止めたときに、早い方・遅い方の瞬間の候補（candidates_ms）を受け取り、どちらにするかを使う側で決めたいとき。",
          "どの tz データ・ICU の版で出した答えかを、結果といっしょに残したいとき（tzdata の tz・icu・matches_pin）。",
          "例外を投げない変換が要るとき。toZoned と fromZoned はどちらも例外を投げず、入力を書き換えません。",
          "現在時刻・乱数・環境の locale を読まず、外の境界を持たない処理として、手元で呼びたいとき。依存パッケージはありません。"
        ],
        "do_not_use_when": [
          "tz データや ICU の版にかかわらず同じ答えが要るとき。答えはこの実行環境の tz データ（2026c）と ICU（78.3）に依り、版が変われば同じ入力でも答えが変わりえます。ほかの版では確かめていません（tzdata.matches_pin が false になります）。",
          "1970 年より前、2100 年より後の時刻を扱うとき。OUT_OF_RANGE で止めます。",
          "閏秒を扱いたいとき。瞬間は閏秒を数えない POSIX の時刻で、60 秒は INVALID_DATE で止めます。",
          "+09:00 のような UTC からのずれの書き方で zone を渡したいとき。zone は IANA の名前の形だけで、ほかは INVALID_ZONE で止めます。",
          "存在しない壁時計を、近い時刻に寄せてほしいとき。NONEXISTENT_LOCAL_TIME で止め、disambiguation に関わらずほかの時刻に寄せません。",
          "Node.js v24.21.0（tz データ 2026c・ICU 78.3）のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "瞬間が安全な整数でないとき（小数・NaN・Infinity・文字列・Date・bigint）INVALID_INSTANT、瞬間や年が 1970〜2100 の外のとき OUT_OF_RANGE で止めます。",
          "zone が文字列でない・64 文字を超える・IANA の名前の形でないとき（ずれの書き方・空白・空の区切り）INVALID_ZONE、形は合うがこの実行環境の Intl が知らないとき UNKNOWN_ZONE で止めます。",
          "壁時計が素のオブジェクトでない、必須の欄が無い、ほかのキー・getter・列挙しないプロパティ・symbol のキーがある、値が整数でないとき、INVALID_LOCAL で止めます。getter は呼びません。",
          "欄の値が範囲の外のとき（2 月 30 日・平年や 2100 年の 2 月 29 日・4 月 31 日・13 月・24 時・60 分・60 秒・1000 ミリ秒・負の値）、繰り上げずに INVALID_DATE で止めます。",
          "夏時間の飛びで存在しない壁時計のとき NONEXISTENT_LOCAL_TIME、重なりで二度ある壁時計のとき（disambiguation が reject（既定））AMBIGUOUS_LOCAL_TIME で止め、候補を添えます。",
          "options が素のオブジェクトでない、ほかのキー・getter がある、disambiguation が reject・earlier・later のどれでもないとき INVALID_OPTIONS、調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
          "壁時計が範囲の中でも、変換した瞬間が範囲の外に出るとき（例: Asia/Tokyo の 1970-01-01 08:59）、OUT_OF_RANGE で止めます。"
        ],
        "what_it_does_not_do": [
          "tz データを自分では持ちません。変換の規則は Node.js の ICU のもので、答えの正しさはこの実行環境の tz データが正しいときだけです。将来の時刻は、その tz データが作られた時点の規則の見込みで、規則の変更（夏時間の廃止など）はその版の tz データが出るまで反映されません。",
          "答えを独立に突き合わせたのは UTC・Asia/Tokyo（1970〜2100 年）と America/New_York・Europe/London（2010〜2035 年）だけです。ほかの zone は往復と性質の検めだけで、答えを別に確かめてはいません。",
          "fromZoned は前後 15 時間を 3 時間ごとに見たずれから候補を作ります。3 時間より短い間隔で変わり目が続く zone・時期では、候補を取りこぼすことがあります（1970〜2100 年の tz データで起きるかは全部は確かめていません）。",
          "何時を締め切りにするか・どの zone を使うか・営業日などの意味の判断はしません。使う側（Kernel・業務）に残ります。",
          "Temporal や外のライブラリとの答えの互換は確かめていません。Proxy の罠は、この部品が調べる操作（getPrototypeOf・ownKeys・getOwnPropertyDescriptor）のものだけを確かめました。",
          "変換できたことは、その時刻が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の日付ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/timezone.js の toZoned(instantMs, zone)・fromZoned(local, zone, options) を呼びます（投げません）。受け取るのは epoch ミリ秒か壁時計、IANA の zone の名前、任意の { disambiguation } です。返すのは { ok: true, errors: [], local か instant_ms, offset_seconds, zone, resolved_zone, tzdata, … } か { ok: false, errors: [{ code }], tzdata }（code は error_taxonomy）。副作用（NONE）・外の接続・資格情報は無く、利用側が用意するものはありません。状態は書式の覚え置き（プロセスの中だけ）で、答えには影響しません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-02",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "機械翻訳の運搬（i18n） — 地域対応の運び継手（Tsugite）",
        "summary": "地域対応の運び継手（Tsugite）は、機械翻訳の運搬（i18n）の項目では、機械翻訳の要求（TranslationRequest）を、pin した基盤と locale（訳す前と後）で検めてから翻訳の基盤へ運び、戻った receipt（TranslationReceipt）を契約どおりに照合する継手（つぎて）です。本文は運ばず、参照だけを運びます。文言の切替やカタログの管理はしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "翻訳の基盤へ要求を運ぶとき、本文をそのまま送ってしまうこと、pin していない基盤や locale へ送ってしまうこと、そして機械翻訳を人が確かめた翻訳や正本と取り違えることを防ぎます。",
        "use_when": [
          "機械翻訳の要求を、本文ではなく参照だけで、pin した翻訳の基盤へ運びたいとき。",
          "訳す前と後の locale を pin した一覧に限り、訳す前と後が同じ locale の要求を送らないようにしたいとき。",
          "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には translation_state・output_ref・human_verification・authoritative_status が添えられます。",
          "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "画面の文言を言語ごとに切り替えたいとき、または多言語の文言カタログを管理したいとき。この継手は機械翻訳の要求を運ぶ継手で、文言の切替・カタログの管理の機能は含みません。",
          "機械翻訳の結果を、人が確かめた翻訳や正本として扱いたいとき。そう名乗る receipt は受理しません。",
          "訳の正しさを確かめたいとき、または訳した文言の公開を承認させたいとき。訳の確認と公開の承認は、外の判断と人・組織の役目です。",
          "時刻の変換・通貨や数の書式・単位の換算・並べ替えなど、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
        ],
        "what_it_checks": [
          "pin していない基盤・locale への要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
          "訳す前と後の locale が同じ要求は送りません。",
          "人が確かめた翻訳（human verification）や正本（authoritative）を名乗る receipt は受理しません。",
          "できた（PRODUCED）のに出力の参照（output_ref）が無い receipt、できなかった（FAILED・UNAVAILABLE）のに出力の参照や効果を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "文言の切替・多言語カタログの管理はしません。この継手は機械翻訳の要求を運ぶ継手で、その機能は含みません。",
          "機械翻訳は、人が確かめた翻訳でも正本でもありません。訳の正しさは確かめません。",
          "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
          "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に TranslationRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED・UNAVAILABLE は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-03",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "通貨整形 — 通貨整形の部品（Primitive）",
        "summary": "通貨整形の部品（Primitive）は、通貨の最小単位で数えた安全な整数（amount_minor）を、部品の中に固定した通貨の表（7 通貨）と locale の表（ja-JP・en-US）で、決まった表記の文字列にする単独の部品です。表示の整形だけで、通貨の換算・丸め方の方針・支払いの判断はしません。受け取れない値は決まったコードで止め、例外を投げません（fail-closed）。",
        "problem": "表示の裏で、動かす環境の国際化 API（ICU）によって表記が変わってしまうこと、桁を捨てたり丸めたりして表記が元の整数に戻らなくなること、表に無い通貨・locale や余分なキー（丸め方などのつもりの指定）を黙って近いものに寄せたり無視したりすることを防ぎます。",
        "use_when": [
          "すでに通貨の最小単位にした安全な整数を、表の通貨（日本・米国の通貨と、EUR・GBP・KRW・CNY・BHD）と locale（ja-JP・en-US）で、記号と桁区切りの付いた表記の文字列にしたいとき（formatMinor）。",
          "動かす環境に左右されない表記が要るとき。時計・乱数・環境の locale・国際化 API（ICU）を使わず、表はこの部品のファイルの中に固定していて、同じ入力からはいつでも同じ text ができます。",
          "表記を最小単位に戻すと、受け取った整数の絶対値にちょうど戻ること（桁を捨てない・丸めない）が要るとき。",
          "受け取れない入力を、どの欄か（field）とコードで、当てはまるものをすべて返してほしいとき。",
          "例外を投げない整形が要るとき。formatMinor は例外を投げず、入力を書き換えず、getter を呼びません。"
        ],
        "do_not_use_when": [
          "小数を含む値や、まだ最小単位にしていない値を渡したいとき。この部品は整数だけを受け取り、丸めもしません。最小単位にするときの丸め方の方針・換算は、使う側か G 系の部品の役目です。",
          "表の 7 通貨と 2 locale のほかを扱いたいとき。UNKNOWN_CURRENCY・UNKNOWN_LOCALE で止め、ja-jp・en-GB・ja などに寄せません。",
          "CLDR・Intl.NumberFormat と同じ出力が要るとき。一致は主張しません。記号の選び方と書き方はこの部品の決まりで、日本の通貨の記号は半角の U+00A5（CLDR の ja の全角 U+FFE5 ではない）、BHD は ISO 4217 のコードと空白（U+0020）です。",
          "記号を数の後ろに置く書き方、ほかの桁区切り（ピリオド・空白・インドの 2 桁区切り）、会計の書き方（括弧の負の数）、全角の数字・記号が要るとき。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "入力がオブジェクトでないとき（null・配列・数・文字列など）、キーが getter・setter のとき、入力を調べること自体が例外になるとき（Proxy の罠など）、INVALID_INPUT で止めます。getter は呼びません。",
          "amount_minor・currency・locale のほかのキーを持つとき（列挙しないキー・symbol のキーを含む）、黙って無視せずに UNKNOWN_FIELD で止めます。継承したキーは読みません。",
          "amount_minor が無い、number でない（bigint・文字列）、整数でない、安全な整数の範囲の外、NaN・Infinity のとき、INVALID_AMOUNT で止めます。",
          "currency が無い、文字列でない、表のコードでないとき（小文字・前後の空白・__proto__ などの継承した名前を含む）、UNKNOWN_CURRENCY で止めます。",
          "locale が無い、文字列でない、表の locale でないとき、近い locale に寄せずに UNKNOWN_LOCALE で止めます。",
          "いくつもの欄が受け取れないとき、errors に amount_minor → currency → locale の順で、当てはまるものをすべて並べます（入力の形が違うとき・余分なキーのときは 1 件だけ）。"
        ],
        "what_it_does_not_do": [
          "通貨整形の部品は表示の整形だけで、通貨の換算・丸め・税・請求・支払いの判断は含みません。それらは G 系の部品や使う側に残ります。",
          "受け取った整数の意味（最小単位にしたときの丸め・通貨の正しさ）は確かめません。受け取った整数をそのまま書きます。",
          "表の 7 通貨と 2 locale のほかは扱いません。ja-JP と en-US の出力は同じで（桁区切りはコンマ・小数点はピリオド・3 桁ごと・記号は数の前）、locale ごとの違いは作りません。",
          "記号は Unicode の文字そのままで、正規化はしません。表は CLDR・各国の慣行の写しではなく、この部品の決まりです。表は凍結していて、使う側は書き換えられません。",
          "表記の文字列ができたことは、額が正しいことでも、請求・支払いをしてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の定義の tsugite-l-03 は検めの型だけで、整形の実装は無く、比べる相手はありません。この部品は整形の実装になる候補です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の国際化ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/currency.js の formatMinor({ amount_minor, currency, locale }) を呼びます（投げません）。受け取るのは 3 つのキーだけのオブジェクトで、amount_minor は最小単位の安全な整数、currency は表の ISO 4217 のコード、locale は ja-JP か en-US です。返すのは { ok: true, errors: [], text } か { ok: false, errors: [{ code, field }], text: null }（code は error_taxonomy）。text は、負のときだけの符号・表の記号・3 桁ごとに区切った整数部・最小単位の桁数の小数部の順です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-04",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "単位変換 — 単位変換の部品（Primitive）",
        "summary": "単位変換の部品（Primitive）は、10 進の文字列の値を、定義どおりの正確な有理数の係数（BigInt の分子・分母）で換算し、小数 0〜18 桁に偶数丸め（half-even）した 10 進の文字列を返す単独の部品です。長さ・質量・圧力は比例、温度（°C・°F・K）は 1 次式で、浮動小数点を使いません。dB の系（dBm↔mW）は v1 にありません。受け取れない値は決まったコードで止め、例外を投げません（fail-closed）。",
        "problem": "換算の途中に浮動小数点の誤差が入ること（\"0.1\" が 1/10 ちょうどでなくなるなど）、次元の違う換算や絶対零度より低い温度を黙って計算してしまうこと、似た綴りの単位や余分なキー（精度などのつもりの指定）を黙って近いものに寄せたり無視したりすることを防ぎます。",
        "use_when": [
          "長さ（m・km・cm・mm・in・ft・mi）、質量（kg・g・lb・oz）、圧力（Pa・kPa・MPa・bar・psi）、温度（K・C・F）の同じ次元の中で、値を換算したいとき（convert）。",
          "換算の結果を、正確な有理数から小数 fraction_digits 桁に half-even で 1 回だけ丸めた値として受け取りたいとき（途中で丸めない）。",
          "係数の出どころが定義値であることが要るとき（in = 0.0254 m、lb = 0.45359237 kg、bar = 100000 Pa、K = °C + 273.15 など）。",
          "派生の値を注入するときの換算の規則として使いたいとき。",
          "時計・乱数・環境の locale に左右されず、外の境界・状態を持たない処理として、手元で呼びたいとき。convert は例外を投げず、入力を書き換えず、getter を呼びません。"
        ],
        "do_not_use_when": [
          "dB の系（dBm↔mW・dB など）を換算したいとき。カタログの一言は「dBm/MPa 等」ですが、dB の系は対数で結果が有理数にならないため v1 に無く、表に無い単位として UNKNOWN_UNIT で止めます。",
          "表の 19 単位のほかを使いたいとき。μm・hPa のような接頭語の組み合わせは自動では作りません。",
          "温度の差（Δ°C ↔ Δ°F）を換算したいとき。温度は絶対温度として換算し、絶対零度より低い値は止めます。",
          "有効数字・誤差（不確かさ）の伝わり方や、half-even のほかの丸めが要るとき。",
          "locale による書き方（小数点のコンマなど）や桁区切りの付いた出力が要るとき。出力は桁区切りの無い 10 進の文字列だけです。",
          "浮動小数点で換算するほかの実装と同じ結果が要るとき。丸めの境目の近くで結果が違うことがあります。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "入力がオブジェクトでないとき、キーが getter・setter のとき、調べること自体が例外になるとき（Proxy の罠など）INVALID_INPUT、5 つのキーのほかのキーを持つとき（列挙しないキー・symbol のキーを含む）UNKNOWN_FIELD で止めます。",
          "value が文字列でない、64 文字を超える、10 進の文法に合わないとき（指数・16 進・NaN・Infinity・+・空白・桁区切り・\"1.\"・\".5\"・先頭の余分な 0・ASCII でない数字）、MALFORMED_VALUE で止めます。",
          "from・to が表の単位でないとき（dBm・mW などの dB の系、\"°C\"・\"KM\"・\"kpa\" などの似た綴り、__proto__ などの継承した名前を含む）、UNKNOWN_UNIT で止めます。",
          "from と to の次元が違うとき、黙って計算せずに INCOMPATIBLE_UNITS で止めます。",
          "温度の値が絶対零度（0 K = -273.15 °C = -459.67 °F）より低いとき、BELOW_ABSOLUTE_ZERO で止めます。絶対零度ちょうどは受け取ります。",
          "fraction_digits が 0〜18 の整数でないとき INVALID_FRACTION_DIGITS、rounding が無いか \"half-even\" ちょうどでないとき UNSUPPORTED_ROUNDING で止めます。",
          "いくつもの欄が受け取れないとき、errors に value → from → to → fraction_digits → rounding の順で、当てはまるものをすべて並べます。"
        ],
        "what_it_does_not_do": [
          "単位変換の部品は、カタログの一言にある dBm を含む dB の系の換算は含みません。v1 は有理数で正確に書ける長さ・質量・圧力（MPa を含む）・温度だけです。",
          "どの値を換算してよいか・突き合わせ（reconcile）は決めません。使う側に残ります。",
          "係数の値は定義から写したもので、オラクルも同じ定義から作ったため、定義そのものの写し違いは両方に同じく入りえます。確かめたのは既知の答えの試験（1 in = 25.4 mm・1 lb = 453.59237 g・1 psi = 6894.757293168361 Pa など）の範囲だけです。",
          "単位の名前は ASCII の記号そのままです（\"C\" は摂氏で、クーロンではありません）。",
          "換算できたことは、元の値や単位の選び方が正しいことでも、その値で設計・判断してよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の定義の tsugite-l-04 は検めの型だけで、換算の実装は無く、比べる相手はありません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の単位換算ライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/unitconvert.js の convert({ value, from, to, fraction_digits, rounding }) を呼びます（投げません）。受け取るのは 5 つのキーだけのオブジェクトで、value は 64 文字までの 10 進の文字列、from・to は表の単位の名前、fraction_digits は 0〜18 の整数、rounding は \"half-even\" ちょうど（省略できない）です。返すのは { ok: true, errors: [], value } か { ok: false, errors: [{ code, field }], value: null }（code は error_taxonomy）。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-05",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "住所候補の照会 — 地域対応の運び継手（Tsugite）",
        "summary": "地域対応の運び継手（Tsugite）は、住所候補の照会の項目では、住所の候補の照会の要求（AddressLookupRequest）を、pin した基盤と地域で検めてから住所の基盤へ運び、戻った receipt（AddressLookupReceipt）を契約どおりに照合する継手（つぎて）です。住所の文面は運ばず、参照だけを運びます。届く住所かの検証も、住んでいる証明もしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "住所の基盤へ照会を運ぶとき、住所の文面をそのまま送ってしまうこと、pin していない基盤や地域へ送ってしまうこと、そして見つかった住所の候補を、届く住所の確認や住んでいる証明と取り違えることを防ぎます。",
        "use_when": [
          "住所の候補の照会を、住所の文面ではなく参照（query_ref）だけで、pin した住所の基盤へ運びたいとき。",
          "照会する地域を、pin した一覧に限りたいとき。",
          "外への効果の無い照会（副作用 NONE）として運びたいとき。",
          "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には lookup_state・candidate_ref・deliverability・residence_proof が添えられます。",
          "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "届く住所かどうか（配送できるか）を検証したいとき。住所の候補は届く住所の確認ではなく、そう名乗る receipt は受理しません。",
          "本人がそこに住んでいることの証明がほしいとき。住所の候補は住んでいる証明ではありません。本人の住所の証明は、外の判断と人・組織の役目です。",
          "住所の表記ゆれをそろえる（正規化する）仕組みそのものがほしいとき。この継手は住所の候補の照会の要求を運ぶ継手で、正規化の機能は含みません。",
          "住所の文面を基盤へ送りたいとき。運ぶのは参照だけで、空白を含む参照は住所の文面とみなして送りません。",
          "電話番号の正規化など、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
        ],
        "what_it_checks": [
          "pin していない基盤・地域への要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
          "照会の参照（query_ref）に空白があれば、住所の文面とみなして送りません。",
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
          "届く住所の確認（deliverability）や住んでいる証明（residence proof）を名乗る receipt は受理しません。",
          "見つからない（NOT_FOUND）のに候補の参照（candidate_ref）を持つ receipt、できなかったのに出力の参照や効果を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "届く住所かの検証も、住んでいる証明もしません。住所の候補は、そのどちらでもありません。届く住所かの確認と本人の住所の証明は、外の判断と人・組織の役目です。",
          "住所の正規化（表記ゆれの統一）の機能は含みません。この継手は住所の候補の照会の要求を運ぶ継手です。",
          "住所の候補の正しさは確かめません。また、住所の文面の検出は空白の有無だけを見る網で、すべての文面を見分けるものではありません。守りの本体は、参照だけを運ぶ作りです。",
          "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
          "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に AddressLookupRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は NONE です（住所の照会）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-06",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "電話番号正規化（E.164） — 電話番号正規化（E.164）の部品（Primitive）",
        "summary": "電話番号正規化（E.164）の部品（Primitive）は、電話番号の文字列を E.164（\"+\" 国番号＋国内の番号 NSN）にする単独の部品です。使う表は、この部品が持つ版つきの小さな表（erabazu-e164-metadata/2026-09-30）だけで、地域は JP（+81）・NANP（US/CA、+1）・GB（+44）の 3 つです。受け取れない値は決まったコードで止めます（fail-closed）。正規化できたことは、番号が存在する・つながる証明ではありません。",
        "problem": "SMS の裏で、宛先の番号の書き方のゆれ（空白・ハイフン・ドット・丸かっこ、国内の形と国際の形）を決まった形にそろえます。そのときに、表に無い国番号・地域を推し量ったり、内線やほかの国際の頭の番号を黙って外したりせず、決まったコードで止めます。",
        "use_when": [
          "JP・NANP（US/CA）・GB の電話番号の文字列を、E.164 の文字列と、表の地域・国番号・NSN に分けて受け取りたいとき（normalize）。",
          "国内の形（先頭が + でない）の番号を、default_region（JP・US・CA・GB）を渡して、国内の頭の番号（trunk）を外した E.164 にしたいとき。",
          "SMS の宛先の形の検めに合う文字列を作りたいとき。この部品の出力は、通知の継手の SMS の宛先の形の検め（/^\\+[1-9][0-9]{6,14}$/）にいつも合います（作業場で 1 回突き合わせたもので、部品の試験には入れていません）。",
          "どの版の表で正規化したかを結果といっしょに残したいとき。どの結果にも metadata_revision が付きます。",
          "例外を投げない呼び方が要るとき。normalize は例外を投げず、入力（options を含む）を書き換えず、options の getter を呼びません。"
        ],
        "do_not_use_when": [
          "番号が存在する・使われている・つながる・誰かのものであることを確かめたいとき。この部品は確かめません（existence_verified はいつも false）。確かめるには別の手段（SMS の確認コードなど）が要ります。",
          "番号計画の全体（割り当て済みの番号帯・市外局番の一覧・番号の種類）を検めたいとき。表の長さと頭の数字に合うだけの番号も受け取ります。",
          "JP・NANP・GB のほかの地域を扱うとき、US と CA を区別したいとき。region は表の見出し（NANP）で、回線のある国ではありません。",
          "内線付きの番号、ほかの国際の頭の番号（00・010・011）で始まる番号、全角の数字、語呂合わせの英字を変換してほしいとき。v1 では受け取りません。",
          "番号計画の改定を自動で追ってほしいとき。表の値は 2026-09-30 に作った保守的なものです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "text が文字列でないとき NOT_A_STRING、64 文字を超えるとき中身を読まずに INPUT_TOO_LONG で止めます。",
          "opts が素のオブジェクトでない、default_region のほかのキー・getter がある、調べること自体が例外になるとき（Proxy の罠など）INVALID_OPTIONS、default_region が JP・US・CA・GB のどれでもないとき UNKNOWN_REGION で止めます。",
          "内線の印（#・;・, または x・ext・extension）があるとき EXTENSION_NOT_SUPPORTED、ASCII の英字があるとき LETTERS_NOT_ALLOWED で、黙って外さずに止めます。",
          "受け取る文字のほかの文字があるとき（全角の数字・タブ・改行・ノーブレークスペース・先頭でない + ・2 つ目の + など）INVALID_CHARACTER、数字が 1 つも無いとき EMPTY_NUMBER で止めます。",
          "国際の形で国番号が 1・44・81 のどれでもないとき UNSUPPORTED_COUNTRY_CODE、国内の形なのに default_region が無いとき MISSING_REGION で止め、推し量りません。",
          "国内の形の頭がその地域の国際の頭の番号（JP の 010・00、GB の 00、NANP の 011）のとき INTERNATIONAL_PREFIX_NOT_SUPPORTED、JP・GB の国内の形の頭に 0 が無いとき MISSING_TRUNK_PREFIX で止めます。",
          "NSN の桁数が表の長さでないとき INVALID_LENGTH、決まった位置の数字が下限より小さいとき（国番号の後の trunk の 0、NANP の市外局番・局番の頭の 0・1）INVALID_NUMBER_PATTERN で止めます。",
          "止めるときのエラーはいつも 1 つで、検める順番は決まっています（error_taxonomy の並びの順）。"
        ],
        "what_it_does_not_do": [
          "電話番号正規化（E.164）の部品は、宛先の文字列を決まった形にするだけです。SMS・通話への送り出しは含まず、それは通知の継手（D）の側です。",
          "正規化できたことは、番号が存在する・つながる・誰かのものである・その人に連絡してよいことの証明ではありません（existence_verified はいつも false）。",
          "文字は ASCII だけを見ます。全角の数字などを半角にする変換・Unicode の正規化はせず、丸かっこの対応・飾りの置き場所は検めません。",
          "表は 3 地域に限った保守的なものです。地域を足すときは表の版を上げ、試験とオラクルを足して再検証する別の作業になります。",
          "正規化できたことは、受け入れてよいことではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の電話番号ライブラリ（libphonenumber など）の写しや使い方の雛形ではありません。"
        ],
        "integration": "src/phone.js の normalize(text, opts) を呼びます（投げません）。受け取るのは 64 文字までの文字列 text と、省略か { default_region } だけの opts（国内の形では必須）です。返すのは { ok: true, errors: [], e164, region, country_code, national_number, metadata_revision, existence_verified: false } か { ok: false, errors: [{ code }], e164: null, … }（code は error_taxonomy）。toE164 は受け取れなければ PhoneError を投げます。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意するものはありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-07",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "暦の参照データ — 地域対応の運び継手（Tsugite）",
        "summary": "地域対応の運び継手（Tsugite）は、暦の参照データの項目では、祝日の暦などの参照データの要求（ReferenceDataRequest）を、pin した基盤・データセット・locale で検めてから参照データの基盤へ運び、戻った receipt（ReferenceDataReceipt）を契約どおりに照合する継手（つぎて）です。参照データを取ってくるだけで、和暦や営業日などの日付の計算はしません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "参照データの基盤へ要求を運ぶとき、pin していない基盤・データセット・locale へ送ってしまうこと、そして取ってきた参照データを、正本の暦や法域の権威と取り違えることを防ぎます。",
        "use_when": [
          "祝日の暦などの参照データを、pin した基盤・データセットから取ってきたいとき。",
          "参照データの locale（locale_ref）を、pin した一覧に限りたいとき。",
          "外への効果の無い取得（副作用 NONE）として運びたいとき。",
          "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には data_state・data_ref・authoritative_status・jurisdiction_authority が添えられます。",
          "基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "和暦への変換や営業日の計算など、日付の計算をさせたいとき。この継手は参照データを取ってくる要求を運ぶ継手で、日付の計算の機能は含みません。",
          "取ってきた参照データを、正本の暦や法域の権威として扱いたいとき。そう名乗る receipt は受理しません。",
          "時刻の変換など、手元の計算で済む地域対応をしたいとき。この継手の対象ではありません。",
          "参照データの正しさを確かめたいとき。正本の暦の判断は、外の判断と人・組織の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える基盤がほしいとき。実の翻訳・住所・参照データの基盤は含みません。"
        ],
        "what_it_checks": [
          "pin していない基盤・locale・データセットへの要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値の要求は送りません。",
          "正本の参照データ（authoritative）や法域の権威（jurisdiction authority）を名乗る receipt は受理しません。",
          "できなかった（FAILED・UNAVAILABLE・NOT_FOUND）のに、出力の参照（data_ref）や効果を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "日付の計算（和暦・営業日）はしません。この継手は参照データを取ってくる要求を運ぶ継手で、日付の計算の機能は含みません。",
          "参照データは、正本の暦でも法域の権威でもありません。ERABAZU は法域の権威を名乗りません。正本の暦の判断は、外の判断と人・組織の役目です。",
          "参照データの正しさは確かめません。",
          "どの結果も承認ではありません（approval: false）。訳せた・見つかったは、公開の承認でも、届く住所の確認でも、正本でもありません。",
          "実の翻訳・住所・参照データの基盤を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の翻訳・地図・暦サービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/localization/v1 でつなぎます。createLocalizationClient({ cfg, port }) の submit(request) に ReferenceDataRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（backends・locales・regions・datasets の pin する一覧）と、基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は NONE です（参照データ）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "L-08",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "照合順序（collation） — 照合順序の部品（Primitive）",
        "summary": "照合順序の部品（Primitive）は、文字列を言語の照合順序で比べ、並べる単独の部品です。照合そのものは Node.js の組み込みの Intl.Collator（ICU）に任せ、この部品は locale（ja・en）と強さ（base・accent・variant）を許可リストで固定し、頼んだとおりに解決されなければ止めます。照合で同じ（sameKey）ことは同一ではありません。受け取れない値は決まったコードで止めます（fail-closed）。",
        "problem": "一覧の裏で、並び順が実行環境の既定の locale に黙って落ちて変わってしまうこと、照合で同じ文字列を同一とみなしてしまうこと、照合で同じ文字列どうしの順が定まらないことを防ぎます。結果に ICU の版を添えるので、どの版の順かを後から確かめられます。",
        "use_when": [
          "文字列の配列を ja か en の照合順序で並べ、結果と ICU の版を受け取りたいとき（sortList。例外を投げません）。",
          "照合で同じ文字列どうしを元の位置の順に残したいとき（sortStable）、または UTF-16 の符号単位の順で決めて全順序にしたいとき（sortTotal）。",
          "2 つの文字列を比べて -1・0・1 のどれかを受け取りたいとき（compare。反対称で -0 を返しません）。",
          "実行環境の既定の locale に依らず、明示した locale だけで並べたいとき。Collator の解決が頼んだとおりでなければ止めます。",
          "入力を書き換えずに並べたいとき。すでに並んでいても新しい配列を返し、要素の getter を呼びません。"
        ],
        "do_not_use_when": [
          "ICU の版にかかわらず同じ順が要るとき。順は実行環境の ICU（78.3）に依り、版が変われば同じ入力でも順が変わりえます。ほかの版では確かめていません（icu.matches_pin が false になります）。",
          "重複の判定や鍵に使いたいとき。照合で同じことは同一ではありません（例: ja の variant で「あ」と「ア」）。",
          "漢字の読み（音読み・訓読み）の順で並べたいとき。ja の漢字の順は ICU の ja の規則（JIS の順）です。読みの順で並べるときは、使う側が読みを渡して並べます。",
          "ja と en のほかの locale（ja-JP・EN・und を含む）を使いたいとき。UNSUPPORTED_LOCALE で止めます。",
          "数の照合（\"10\" と \"2\" を数として比べる）が要るとき。この部品はしません。",
          "Node.js v24.21.0（ICU 78.3）のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "opts が素のオブジェクトでない、キーが locale・strength（sortList は order も）ちょうどでない、order が stable・total のどちらでもないとき、INVALID_OPTIONS で止めます。",
          "locale が ja・en のどちらでもないとき UNSUPPORTED_LOCALE、strength が base・accent・variant のどれでもないとき UNSUPPORTED_STRENGTH で止めます。",
          "Intl.Collator が頼んだ locale と固定の設定のとおりに解決されないとき（ICU にその locale が無いなど）、ほかの locale に黙って落ちずに LOCALE_NOT_AVAILABLE で止めます。",
          "比べる値・要素が文字列でないとき（String オブジェクト・穴・getter の要素を含む）NOT_A_STRING、65,536 符号単位を超えるとき STRING_TOO_LONG で止め、要素なら index に位置を返します。",
          "list が配列でないとき（配列のようなオブジェクト・Set を含む）NOT_AN_ARRAY、100,000 要素を超えるとき LIST_TOO_LONG で止めます。",
          "sortList で値を調べること自体が例外になるとき（Proxy の罠など）、INSPECTION_FAILED を返し、例外を投げません。"
        ],
        "what_it_does_not_do": [
          "照合順序を自分では持ちません。照合順序は ICU（78.3）のもので、独立には作り直していません。ファズのオラクルも同じ ICU の Collator を使うので、ICU の順が正しいかは確かめていません（確かめたのは並べ替えの性質・安定性・同点の決め方・止めるコードと、ja・en の既知の答え数組）。",
          "並び順が使う人の期待（辞書・電話帳・読みの順など）と同じとは限りません。どの順で見せるかの判断は使う側に残ります。",
          "sortTotal の符号単位の比べで、Unicode の正規化はしません。",
          "Proxy の罠を INSPECTION_FAILED に変えるのは sortList だけです。投げる関数（compare・sameKey・sortStable・sortTotal）は罠の例外をそのまま外に出します。",
          "並べられたことは、その順が正しいことでも、受け入れてよいことでもありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の並べ替えのライブラリの使い方の雛形ではありません。"
        ],
        "integration": "src/collation.js の sortList(list, { locale, strength, order }) を呼びます（投げません）。受け取るのは 100,000 要素までの文字列の配列と、3 つのキーだけの素のオブジェクトです。返すのは { ok: true, errors: [], sorted, icu, … } か { ok: false, errors: [{ code, index? }], icu }（code は error_taxonomy）。compare・sameKey・sortStable・sortTotal は受け取れない値で CollationError を投げます。副作用（NONE）・外の接続・資格情報は無く、利用側が用意するものはありません。状態は Collator の覚え置き（プロセスの中だけ）で、答えに影響しません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-01",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "ビデオ通話の呼制御（WebRTC/SFU） — 双方向の通信の運び継手（Tsugite）",
        "summary": "双方向の通信の運び継手（Tsugite）は、ビデオ通話の呼制御（WebRTC/SFU）の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。映像・音声そのもの（WebRTC・SFU のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
        "use_when": [
          "ビデオ通話の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
          "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
          "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
          "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "映像・音声そのもの（WebRTC・SFU のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、映像・音声を運ぶ機能は含みません。",
          "映像・音声の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
          "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
        ],
        "what_it_checks": [
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
          "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
          "合図の op と合わない呼の状態の receipt は受理しません。",
          "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "映像・音声そのもの（WebRTC・SFU のメディア）は運びません。ビデオ通話の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
          "映像・音声の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
          "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
          "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
          "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の通話・チャットサービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-02",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "音声通話の呼制御（SIP） — 双方向の通信の運び継手（Tsugite）",
        "summary": "双方向の通信の運び継手（Tsugite）は、音声通話の呼制御（SIP）の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。音声そのもの（SIP のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
        "use_when": [
          "音声通話の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
          "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
          "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
          "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "音声そのもの（SIP のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、音声を運ぶ機能は含みません。",
          "音声の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
          "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
        ],
        "what_it_checks": [
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
          "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
          "合図の op と合わない呼の状態の receipt は受理しません。",
          "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "音声そのもの（SIP のメディア）は運びません。音声通話の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
          "音声の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
          "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
          "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
          "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の通話・チャットサービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-03",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "プレゼンス（公開を運ぶ） — 双方向の通信の運び継手（Tsugite）",
        "summary": "双方向の通信の運び継手（Tsugite）は、プレゼンスの項目では、プレゼンスの公開の要求（PresencePublishRequest）を、pin した本人（プレゼンスを出してよい本人の参照）で検めてから通信基盤へ運び、戻った receipt（PresencePublishReceipt）を契約どおりに照合する継手（つぎて）です。「オンライン」を出しても、人がいるとは限りません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "プレゼンスを通信基盤へ出すとき、出してよいと決めていない本人のプレゼンスを出してしまうこと、本人の参照にメールアドレスや電話番号を置いてしまうこと、そして「オンライン」を人がいることと取り違えることを防ぎます。",
        "use_when": [
          "在席・状態（プレゼンス）の公開を、pin した本人の参照（仮名）に限って通信基盤へ運びたいとき。",
          "戻った receipt を、request_id・request_digest・参照の一致で照合してから受け取りたいとき。DONE の result には publish_state・human_availability が添えられます。",
          "プレゼンスの公開を、取り消せる外への効果（EXTERNAL_REVERSIBLE）として扱いたいとき。",
          "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "「オンライン」を、人がそこにいることの確認として使いたいとき。そう名乗る receipt は受理しません。",
          "出してよいと決めていない本人のプレゼンスを出したいとき。pin していない本人のプレゼンスは出しません。",
          "本人の参照にメールアドレスや電話番号をそのまま使いたいとき。その要求は送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
        ],
        "what_it_checks": [
          "pin していない本人のプレゼンスは出しません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
          "本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
          "人がいること（human availability）を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "「オンライン」を出しても、人がいるとは限りません。人がいることは確かめません。",
          "プレゼンスを出してよい本人を決めません。どの本人を出すかは、利用側が pin する一覧（presence_subjects）で決めます。",
          "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
          "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の通話・チャットサービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に PresencePublishRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-04",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "ライブ字幕伝送（字幕の区切りを digest で運ぶ） — 双方向の通信の運び継手（Tsugite）",
        "summary": "双方向の通信の運び継手（Tsugite）は、ライブ字幕伝送の項目では、字幕の区切りをリアルタイムのメッセージの要求（RealtimeMessageRequest）として、pin したチャンネルで検めてから通信基盤へ運び、戻った receipt（RealtimeMessageReceipt）を契約どおりに照合する継手（つぎて）です。字幕の本文は運ばず、digest だけを運びます。字幕の正しさは確かめません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "字幕の区切りを通信基盤へ運ぶとき、本文を digest の欄に置いて送ってしまうこと、pin していないチャンネルへ送ってしまうこと、そして届いた・読まれたを、本人の確認や理解・権限と取り違えることを防ぎます。",
        "use_when": [
          "ライブ字幕の区切りを、本文ではなく digest（payload_digest）で、pin したチャンネルへ運びたいとき。",
          "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には delivery_state・delivered・identity_verification・comprehension・authority_assertion が添えられます。",
          "受け手の参照にメールアドレス・電話番号の形を置かずに運びたいとき。",
          "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "字幕の本文そのものを運びたいとき。この継手は字幕の区切りを digest で運ぶ継手で、本文は運びません。本文を payload_digest に置いた要求は送りません。",
          "字幕の正しさを確かめたいとき。字幕の正しさは確かめず、外の判断と人・組織の役目です。",
          "字幕の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
          "届いた・読まれたを、本人の確認や理解・権限として扱いたいとき。そう名乗る receipt は受理しません。",
          "届いたメッセージを取り消したいとき。メッセージの副作用は取り消せない外への効果（EXTERNAL_IRREVERSIBLE）です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
        ],
        "what_it_checks": [
          "pin していないチャンネルへの要求は送りません。設定の一覧が空のとき、空の項目があるときも送りません（空の一覧は何も許しません）。",
          "本文を payload_digest に置いた要求、形の崩れた digest の要求は送りません。",
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
          "受け手の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
          "本人の確認（identity verification）・理解（comprehension）・権限（authority）を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "字幕の本文は運びません。運ぶのは字幕の区切りの digest だけです。字幕の本体の運搬・品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
          "字幕の正しさ、届いたメッセージの中身は確かめません。",
          "届いた・読まれたは、本人の確認でも理解・同意でも権限でもありません。",
          "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
          "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の通話・チャットサービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に RealtimeMessageRequest を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_IRREVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-05",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "大容量・再開可能転送 — 大容量・再開可能転送継手（Tsugite）",
        "summary": "大容量・再開可能転送継手（Tsugite）は、大きな中身を 1 つの object として保管先へ送るために、転送を始め、区切り（chunk）を committed_offset から順番どおりに送り、途中で止まったら再開位置を問い合わせて続け、全部そろって中身の sha256 が始めに宣言した payload_digest と同じときだけ確定する継手（つぎて）です。区切りの受け取りや確定を、中身の受け入れとは扱いません（fail-closed）。",
        "problem": "巨大なファイルを区切って送るとき、途中の障害で結果が分からない区切りを二重に積んだり、欠けや違う中身のまま「送れた」と扱ったりすることを防ぎます。区切りの長さと sha256 は送る前に自分で確かめ、全体の sha256 は受け取られた区切りだけで積み上げます。",
        "use_when": [
          "1 回で送れない大きさのファイルを、区切って保管先へ送りたいとき。",
          "転送が途中で止まったとき、再開位置（committed_offset）を問い合わせて続きから送りたいとき。",
          "全部そろった中身の sha256 が始めの payload_digest と同じことを確かめてから、確定したいとき。",
          "結果の分からない区切りを、二重に積まずに解きたいとき。再開位置の問い合わせか、同じ要求の再送（lookup でも解けます）で解きます。",
          "状態の食い違う receipt が出たら、人が確かめるまで転送を止めたいとき。"
        ],
        "do_not_use_when": [
          "区切りを並列に送ったり、順不同で受け取ったりしたいとき。契約 v1 の範囲外で、この継手は送りません。",
          "1 回で送れる大きさの保管だけが要るとき。それはオブジェクト保管（C-01）の継手です。",
          "確定を、中身が正しい・受け入れてよいという判断として扱いたいとき。確定は中身の受け入れではありません（content_acceptance は NOT_DERIVED）。",
          "別のプロセスからの再開や、再起動をまたぐ再開をしたいとき。転送の状態はこの継手の中だけにあり、再起動で消えます。",
          "すぐに使える保管先がほしいとき。実の保管の基盤には一度も接続しておらず、実の転送は 0 回です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "区切りの順番を検めます。committed_offset から順番どおりでない区切り（飛ばした offset、戻った offset、全体の大きさを超える区切り）、最後のほか chunk_size でない区切りは送らず、基盤へも送りません。",
          "区切りの中身を検めます。中身の長さまたは sha256 が宣言と違う区切りは送りません。送る前に自分で確かめます。",
          "確定を検めます。全部の区切りがそろう前の確定と、始めの payload_digest と違う sha256 での確定は送りません。COMPLETED は MATCH の証拠と stored_digest がないと受け取りません。",
          "設定の範囲を検めます。許していない content-type、上限を超える全体の大きさ・区切りの大きさ、allow_worm が true でないときの WORM（取り消せない確定）は送りません。",
          "転送の状態を検めます。未解決の区切りがあるうちの別の送信、期限切れ・閉じた・確定した・中止した転送への送信は送りません。中止の後は再開できません。",
          "receipt を照合します。状態の食い違うもの、外への効果を増やすもの、別の要求の request_digest を名乗るもの、受け入れを名乗る完了は受理せず、受理できない receipt で停止します（HALTED）。",
          "基盤の障害を検めます。障害で結果が分からなくなった区切りは UNKNOWN とし、再開位置の問い合わせの答えが合わなければ受理しません。何が残ったか分からない効果は受け取りません。"
        ],
        "what_it_does_not_do": [
          "巨大ファイルそのものの保管はしません。この継手は、保管の基盤へ区切りを運び、返る receipt を照合するだけです。実の保管の基盤には一度も接続しておらず、実の転送は 0 回です。",
          "並列の分割も、順不同の受け取りもしません。契約 v1 の範囲外で、区切りは committed_offset から順番どおりだけです。",
          "中身そのものの検査はしません。確認・受け入れ・保持の判断は、外の判断と人・組織の役目です。確定は中身の受け入れではありません（content_acceptance は NOT_DERIVED）。",
          "転送の状態を永続化しません。状態はこの継手の中だけで、再起動で消えます。別のプロセスからの再開もしません。",
          "期限（expires_at）は形だけを検めます。期限切れは基盤の答えから知ります。",
          "どの結果も承認ではありません（approval: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のクラウド保管サービスの分割アップロードの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/object-store-resumable/v1でつなぎます。createResumableClient({ cfg, port }) の submit(request, bytes) に TransferStart・ChunkSend・ResumeQuery・TransferComplete・TransferAbort を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれにも approval: false）。bytes は ChunkSend だけ。用意するもの: 基盤への port（submit、任意で lookup）。副作用は WORM の確定と中止が EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "M-06",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "画面共有の呼制御 — 双方向の通信の運び継手（Tsugite）",
        "summary": "双方向の通信の運び継手（Tsugite）は、画面共有の呼制御の項目では、呼の合図（発信・応答・終了）の要求（CallSignalRequest）を検めてから通信基盤へ運び、戻った receipt（CallSignalReceipt）を契約どおりに照合する継手（つぎて）です。画面そのもの（画面共有のメディア）は運びません。検めが一つでも外れれば、送らず、受理しません（fail-closed）。",
        "problem": "呼の合図を通信基盤へ運ぶとき、参加者の参照にメールアドレスや電話番号を置いてしまうこと、合図の op と合わない呼の状態を受け取ってしまうこと、そしてつながったことを参加者の同意や本人の確認と取り違えることを防ぎます。",
        "use_when": [
          "画面共有の発信（INITIATE）・応答（ANSWER）・終了（END）の合図を、通信基盤へ運びたいとき。",
          "戻った呼の状態が合図の op に対応しているか（INITIATE→RINGING・CONNECTED、ANSWER→CONNECTED、END→ENDED。どれも FAILED は可）を照合したいとき。",
          "戻った receipt を、request_id・request_digest・参照と op の一致で照合してから受け取りたいとき。DONE の result には op・call_state・consent・identity_verification が添えられます。",
          "通信基盤の障害で結果が不明（UNKNOWN）になっても、同じ request_id を二度送らず、lookup で解きたいとき。"
        ],
        "do_not_use_when": [
          "画面そのもの（画面共有のメディア）を運びたいとき。この継手は呼の合図を運ぶ継手で、画面を運ぶ機能は含みません。",
          "画面の品質・遅延・暗号化を確かめたいとき。この部品の対象外で、確かめていません。",
          "つながったことを、参加者の同意や本人の確認として扱いたいとき。そう名乗る receipt は受理しません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える通信基盤がほしいとき。実の通信基盤は含みません。"
        ],
        "what_it_checks": [
          "参照が空のもの・パスや .. を含むもの、追加のフィールド、決まった値（const）と違う値、契約に無い op の要求は送りません。",
          "参加者・本人の参照にメールアドレス・電話番号の形があれば送りません（検出はその代表的な形だけです）。",
          "合図の op と合わない呼の状態の receipt は受理しません。",
          "同意（consent）や本人の確認（identity verification）を名乗る receipt は受理しません。",
          "意図と違う副作用の要求は送りません。失敗（FAILED）なのに効果を名乗る receipt、意図より外への効果を増やす receipt、効果が不明（UNKNOWN）な receipt は受理しません。",
          "別の要求の receipt（request_digest の偽り）は受理せず、受理できない receipt が来たら停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "画面そのもの（画面共有のメディア）は運びません。画面共有の項目で運ぶのは、呼の合図（発信・応答・終了）だけです。",
          "画面の品質・遅延・暗号化は、この部品の対象外で、確かめていません。",
          "つながったは、参加者の同意でも本人の確認でもありません。本人の確認と同意は、外の判断と人・組織の役目です。",
          "どの結果も承認ではありません（approval: false）。届いた・読まれた・つながったは、本人の確認でも同意でも権限でもありません。",
          "実の通信基盤（WebRTC・SFU・SIP・プレゼンス・メッセージのサービス）を含まず、一度も接続していません。port の実装と pin する一覧は、接続するときに別の関門で決めます。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の通話・チャットサービスの使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/communication/v1 でつなぎます。createCommunicationClient({ cfg, port }) の submit(request) に CallSignalRequest（op は INITIATE・ANSWER・END）を渡すと、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false）。利用側が用意するもの: cfg（channel_refs・presence_subjects）と、通信基盤への port（submit と、任意で lookup）。秘密は port 側が持ちます。副作用は EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-01",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "サービス発見・登録 — サービス発見・登録の継手（Tsugite）",
        "summary": "サービス発見・登録の継手（Tsugite）は、外の登録の基盤（registry / control plane）へ、サービスの instance の登録（REGISTER、lease つき）・登録の取り消し（DEREGISTER）・lease の延長（HEARTBEAT）・照会（QUERY）の要求（RegistryRequest）を検めてから運び、返った receipt（RegistryReceipt）を契約どおりに照合する継手（つぎて）です。照会の結果は時刻つきの観測で、載っている endpoint を正しい・健全・許可された・本物とは扱いません（fail-closed）。",
        "problem": "相手の所在を外の登録の基盤に頼るとき、期限の過ぎた lease を登録済みと扱うこと、古い instance を健全なまま返すこと、そして「載っている」ことを正しさ・許可・身元と取り違えることを防ぎます。lease の期限と照会の古さは、注入した時計で確かめます。",
        "use_when": [
          "マイクロサービスの instance を、外の登録の基盤に lease つきで登録し、HEARTBEAT で延ばし、不要になったら取り消したいとき。",
          "相手の所在を照会し、古い instance も隠さずに、stale: true と effective_health: STALE の印つきで受け取りたいとき。",
          "使ってよい registry・service・登録してよい endpoint・ttl の上限を設定で決め、その外へは送りたくないとき。",
          "lease の期限や照会の古さを、登録の基盤の言い分ではなく、注入した時計で確かめたいとき。"
        ],
        "do_not_use_when": [
          "手元だけの静的な名前解決がほしいとき。この継手は外の登録の基盤へ運ぶ継手で、手元の名前解決は別の Primitive の範囲です。",
          "照会で得た endpoint を、そのまま本物・健全・呼んでよい相手として使いたいとき。この継手は確かめません（endpoint_identity は NOT_VERIFIED、correctness・authority は NOT_DERIVED）。",
          "lease の状態を再起動の後も持ち越したいとき。状態はこの継手の中だけで、再起動の後は前に登録した instance の HEARTBEAT・DEREGISTER を送りません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。",
          "すぐに使える登録の基盤への接続がほしいとき。実の登録の基盤には一度も接続しておらず、port の実装・資格情報・設定の値は接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "設定を検めます。設定が無い、一覧に無い registry・service・endpoint、register_endpoints・max_ttl_seconds の無い REGISTER、stale_after_seconds の無い QUERY は送りません。",
          "ttl を検めます。整数でない ttl、設定の上限を超える ttl、86400 を超える ttl・上限の設定は送りません。",
          "時計を検めます。時計が無い・壊れていれば何も送らず、receipt も受け取りません。",
          "lease の扱いを検めます。まだ有効な lease がある instance への REGISTER、lease の無い・期限の過ぎた lease への HEARTBEAT、この継手が登録していない instance の DEREGISTER は送りません。",
          "参照と要求の形を検めます。.. や絶対パス・ドライブ・制御文字を含む参照、Ref でない request_id、形の悪い service_name、未知の op、op が使わない欄は送りません。",
          "receipt を照合します。request_digest の偽り、request_id・registry・service_name・op の食い違い、表の外の状態、効果を増やす・不明な効果は受理せず、停止します（HALTED）。",
          "lease と照会の中身を照合します。依頼した ttl より長い lease、時刻の形でない lease、未来の last_seen_at、同じ instance が二度載った照会、要求の前や今より後の観測時刻の receipt は受け取りません。",
          "receipt が正しさや許可を導いていたら（correctness・authority が NOT_DERIVED でない）受理しません。"
        ],
        "what_it_does_not_do": [
          "載っている endpoint が本物か（身元・証明書）、正しい・健全・呼んでよいかは確かめません。observed_health は登録の基盤の観測をそのまま運ぶだけです。身元の確かめは、たとえば B-09 の証明書と呼ぶ側の信頼の方針の役目です。",
          "登録・照会できたことは承認ではありません（approval: false）。呼んでよいかの判断は、外の判断と人・組織の役目です。",
          "instance の lease_expires_at の長さが妥当かは確かめません（契約に基準がありません）。遠い先の lease を返す基盤では、古い instance が古いと出ないことがあります。他人の instance の DEREGISTER はしません。",
          "時計そのものの正しさと、登録の基盤の時計との実際のずれは確かめません。",
          "lookup の無い port で、結果不明の送信がもう検めを通らないときは、未解決のまま止まります。効果が UNKNOWN の FAILED も受け取らず停止し、登録が残ったかは人が確かめます。",
          "登録の基盤の認証に使う資格情報は持ちません。資格情報は port 側が持ちます（HELD_BY_PORT）。契約も変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の登録の基盤（サービスメッシュ・クラウドの名前解決など）の使い方の雛形ではありません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/service-registry/v1 でつなぎます。createServiceRegistryClient({ cfg, port, clock }) の submit(request) に RegistryRequest を渡すと、DONE（result に op ごとの事実。QUERY では instances・endpoint_identity）・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側が用意するもの: 登録の基盤への port（submit、任意で lookup）と時計 clock。副作用: QUERY は NONE、ほかの 3 つは EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-02",
        "category": "N/連携基盤",
        "kind": "Component",
        "title": "API ゲートウェイ・ルーティング — API ゲートウェイ・ルーティングの部品（Component）",
        "summary": "API ゲートウェイ・ルーティングの部品（Component）は、ゲートウェイの経路の表を、手元で検めて（部品(Primitive) route-config）から、ゲートウェイへ反映する（継手 gateway-config）ために束ねる部品です。変更は 2 段で、prepareApply・prepareRollback が人・組織の承認する対象（authority subject）とその digest を返し、その digest を含む根拠を添えて apply・rollback を呼びます。根拠の無い変更、根拠の対象と違う変更、検めが断った表は送りません（fail-closed）。反映できたことは、流れが正しいことでも変更の承認でもありません。",
        "problem": "認証や流量の集約点であるゲートウェイの裏で、検めを通っていない表や、検めた digest と違う表を反映してしまうこと、人・組織の根拠の無い変更や別の変更への承認で反映してしまうこと、結果の分からない変更を別の要求で送り直してしまうことを防ぎます。",
        "use_when": [
          "経路の表を手元で検め（形・参照・衝突・隠れた経路）、検めが通った表だけをゲートウェイへ APPLY したいとき。checkRoutes に route-config の check、gateway に gateway-config の継手のクライアントを注入します。",
          "変更を 2 段に分けたいとき。prepareApply・prepareRollback は何も送らずに、承認する対象（gateway_ref・op・base_version_ref・config_digest または rollback_to_version_ref）とその digest を返し、apply・rollback はその digest を含む人・組織の根拠（origin: HUMAN か ORGANIZATION、evidence_ref、subject_digest）を受け取ります。",
          "APPLY に、手元の検めの出力から継手が作った証跡（validation_evidence）を載せ、続けて人・組織の根拠を継手へ渡してから送りたいとき。",
          "前の版へ戻す（rollback）ときも、人・組織の根拠を継手へ渡してから送りたいとき。",
          "今の版を効果なしで観測し（observe、STATUS）、rollout_of_request_id を渡したときは、その反映が終わったかを継手の結果のまま受け取りたいとき。observe は保留中でも送れます。",
          "結果の分からない変更を保留にし、resolvePending で同じ要求でだけ送り直したいとき。base_version_ref が二重の変更を防ぎます。"
        ],
        "do_not_use_when": [
          "反映できたこと（APPLIED）を、流れが正しいことや変更の承認として使いたいとき。どの結果にも approval: false・authority: NOT_DERIVED・traffic_correctness: NOT_DERIVED が付きます。",
          "人・組織の根拠なしに経路を変えたいとき。経路の変更（APPLY・ROLLBACK）には人・組織の根拠が要り、この部品は根拠を作らず、呼び出し側から受け取ります。",
          "根拠の中身（誰が・いつまで）をこの部品に確かめさせたいとき。この部品は、対象の digest が合うことだけを確かめて継手へ渡します。",
          "リクエストの振り分け（入口の交通整理）そのものを行う部品が要るとき。この部品は経路の表を検め、振り分けを変える反映を束ねた継手に送らせるだけです。",
          "ゲートウェイが検めと同じ規則で表を読むこと、同じ digest を計算することを当てにしたいとき。どちらも確かめません（route-config の限界のまま）。",
          "本物のゲートウェイで確かめた部品が要るとき。本物のゲートウェイ・制御面には一度も接続していません。",
          "再起動をまたいで保留の変更を続けたいとき。状態はこのプロセスの中だけで、再起動で保留の要求は消えます。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・参照が不正・port の関数が無いとき INVALID_OPTIONS、入力の形が違うとき INVALID_INPUT、入力や port の結果の getter が例外を投げたとき INSPECTION_FAILED で止めます。",
          "手元の検めが表を断ったとき ROUTES_REFUSED（route_errors を添える）、検めの結果の config_digest の形が違うとき（domain・algorithm・64 桁の 16 進・target）CHECK_RESULT_INVALID、検めの関数が投げたとき PORT_FAILED で、APPLY を送りません。",
          "根拠が無い・欄の過不足・出どころが HUMAN か ORGANIZATION でない・参照が不正・subject_digest が 64 桁の 16 進でないとき NO_AUTHORITY_EVIDENCE、根拠の subject_digest がその場で計算し直した対象の digest と違うとき（別の変更の承認）AUTHORITY_SUBJECT_MISMATCH で、APPLY・ROLLBACK を送りません。",
          "継手が検めの証跡を返さないとき EVIDENCE_REFUSED、根拠の記録に true ちょうどを返さないとき AUTHORITY_REFUSED で、送りません。",
          "継手の結果が分からないとき（UNKNOWN・例外）、変更を GATEWAY_UNKNOWN で保留にし、保留の間は resolvePending と observe のほかを CHANGE_PENDING で止めます。保留が無いのに resolvePending を呼ぶと NOTHING_PENDING です。",
          "継手が送らなかったとき GATEWAY_REFUSED（gateway_errors を添える）、receipt を照合できず停止したとき GATEWAY_HALTED で、状態を変えずに伝え、保留も残しません。",
          "applied は継手の結果が applied: true のときだけ、rollout_completed は継手の結果が true ちょうどのときだけ true にします。observe（STATUS）は効果なし（intended_side_effect NONE）で送ります。",
          "timestamp が date-time の文字列を返さない・投げたとき CLOCK_FAILED、前の操作が終わっていないとき BUSY で止めます（同時に 1 つの操作だけ）。"
        ],
        "what_it_does_not_do": [
          "API ゲートウェイ・ルーティングの部品は継手ではありません。自分の Request/Receipt は無く、振り分けを変えるという外への効果は、束ねた gateway-config の継手が自分の契約（gateway-config/v1）で起こします。この部品自身は外へ触れません。",
          "束ねた部品の意味（検めが通った ≠ 流れが正しい、反映できた ≠ 承認）は、それぞれの部品の側に残ります。",
          "反映できたことは、流れが正しいことでも変更の承認でもありません（approval: false・authority: NOT_DERIVED・traffic_correctness: NOT_DERIVED、derives_authority: false）。",
          "人・組織の根拠を作りません。経路の変更の根拠は呼び出し側から受け取り、根拠の中身（誰が・いつまで）は確かめません。",
          "ACCEPTED・APPLYING・PARTIAL の後に反映が終わったかは、observe に rollout_of_request_id を渡して継手の結果のまま返すだけで、この部品は判定しません。",
          "本物のゲートウェイ・制御面には一度も接続していません。単体試験とファズは代役で、本物の 2 部品のコードとの結線は、継手の試験用のゲートウェイの代役を相手に確かめました。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のゲートウェイ製品の使い方の雛形ではありません。"
        ],
        "integration": "src/routing.js の createGatewayRouting(options) で作ります。受け取るのは gateway_ref・request_prefix・ports・任意の timestamp で、{ ok, errors, instance } を返します。各操作は { ok, errors, approval: false, record_pending, last_change, … } を返します。利用側が用意する port は checkRoutes（route-config の check）と gateway（gateway-config の継手のクライアント。submit と、証跡・根拠を渡す 2 つの関数）です。副作用は束ねた継手が起こし（EXTERNAL_REVERSIBLE）、資格情報は port が持ちます。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-03",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Webhook 購読レジストリ — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、Webhook 購読（WebhookSubscription）の登録・更新・取り消しの要求を、pin した Webhook の宛先の一覧で検めてから連携先へ送り、戻った receipt を契約どおりに照合する継手（つぎて）です。宛先・参照・receipt のどれかが外れたら、送らず、受理しません（fail-closed）。",
        "problem": "通知先（Webhook の宛先）を外部に登録するとき、任意の callback URL を差し込まれること、receipt で宛先を差し替えられること、そして「登録できた」を「通知が届いた」と取り違えることを防ぎます。",
        "use_when": [
          "通知先（Webhook の宛先）の登録・管理を、外部の連携先に対して行いたいとき。宛先は、あらかじめ pin した一覧の中からだけ選びます。",
          "購読の登録（SUBSCRIBE）・更新（UPDATE）・取り消し（REVOKE）の結果を、receipt の状態（REGISTERED・UPDATED・REVOKED か FAILED）で照合してから扱いたいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。",
          "照合できない receipt を受け取ったら、以後の送信を止めたいとき（RECEIPT_REJECTED のあとは HALTED）。"
        ],
        "do_not_use_when": [
          "Webhook の配送・署名の検証・受領をしたいとき。この継手が扱うのは購読の登録だけです。",
          "購読の receipt を、通知が届いた証拠として使いたいとき。receipt が言えるのは登録の段階（SUBSCRIPTION_REGISTERED）までです。",
          "任意の callback URL を利用者に指定させたいとき。pin していない宛先には送りません。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "宛先を検めます。pin した Webhook の宛先の一覧に無い宛先（callback URL の差し込み）には送りません。空の一覧は何も許しません。",
          "receipt の宛先を照合します。宛先が差し替えられていたら受理せず、停止します（HALTED）。",
          "配送や受領まで言う購読の receipt は受理しません。状態が操作に対応しない（SUBSCRIBE→REGISTERED・UPDATE→UPDATED・REVOKE→REVOKED のどれでもなく FAILED でもない）receipt も受理しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。購読の効果は EXTERNAL_REVERSIBLE（FAILED は NONE）です。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "Webhook の配送・署名の検証・受領はしません。この継手の範囲外です（購読の登録だけ）。",
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（WebhookSubscription）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には subscription_state・subscription_ref・lifecycle_stage が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_REVERSIBLE（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-04",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "データ取込（ETL）コネクタ — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、外部から自系へのデータ取込（DataImport）の要求を、pin した取込元・スキーマ・対応表で検めてから連携先へ送り、戻った receipt の件数と却下の参照を契約どおりに照合する継手（つぎて）です。一部失敗は成功にも失敗にも潰さず、そのまま返します（fail-closed）。",
        "problem": "外部からデータを取り込むとき、pin していない取込元や対応表から取り込んでしまうこと、そして件数や一部の失敗をごまかして「全部取り込めた」と扱ってしまうことを防ぎます。",
        "use_when": [
          "外部から自系へのデータの取り込み（移行・同期）を、pin した取込元・スキーマ・対応表の範囲に限って行いたいとき。",
          "取込の結果を、件数（received = accepted + rejected）と却下の参照（rejected_record_refs）で照合してから扱いたいとき。",
          "一部だけ失敗した取込を、一部失敗（PARTIAL）のまま受け取りたいとき。",
          "取り消せない取込を、設定で明示して許したとき（allow_irreversible: true）だけ送りたいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "取り込んだデータの意味や対応表が正しいかを確かめたいとき。この継手が見るのは件数と参照だけです（semantic_correctness: NOT_VERIFIED）。",
          "取込の突き合わせの判断を任せたいとき。突き合わせは核（reconcile-facts）の役目です。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "取込元・スキーマ・対応表を検めます。pin した一覧に無いものは送りません。空の一覧は何も許しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "取り消せない取込は、設定が allow_irreversible: true のときだけ送ります。そうでなければ送りません。",
          "件数を照合します。received = accepted + rejected にならない receipt、却下の件数と却下の参照の数が合わない receipt、却下の参照に重複がある receipt は受理しません。",
          "結果の状態を照合します。受け入れと却下の片方しかない PARTIAL、却下があるのに完了とする receipt、受け入れがあるのに FAILED とする receipt は受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。取込の効果は EXTERNAL_*（FAILED は NONE）です。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "取り込んだデータの意味の正しさ・対応表の正しさは確かめません（件数と参照だけ）。",
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（DataImport）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には partial_state・件数・rejected_record_refs・semantic_correctness: NOT_VERIFIED が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_*（FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-05",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "データ書出コネクタ — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、自系から外部へのデータ書出し（DataExport）の要求を、pin した行き先・スキーマ・profile で検めてから送り、戻った receipt を契約どおりに照合する継手（つぎて）です。「作れた」「届けた」「受け取られた」を分けて返し、合わない receipt は受理しません（fail-closed）。",
        "problem": "自系のデータを外部へ書き出すとき、pin していない行き先へ送ってしまうこと、そして「作れた」だけで「相手に届いた」「相手が受け取った」と扱ってしまうことを防ぎます。",
        "use_when": [
          "自系から外部へのデータの書き出しを、pin した行き先・スキーマ・profile の範囲に限って行いたいとき。書き出しの選択も、任意で pin できます（selection_refs）。",
          "書出しの結果を、作れた（GENERATED）・届けた（DELIVERED）・受け取られた（ACCEPTED）に分けて扱いたいとき。DONE には delivery_state・delivered・destination_accepted が添えられます。",
          "取り消せない書出しを、設定で明示して許したとき（allow_irreversible: true）だけ送りたいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "作れた（GENERATED）ことを、相手に届いた証拠として使いたいとき。この継手は 3 つの段階を分けて返します。",
          "書き出すデータの意味の正しさを確かめたいとき。意味の正しさは核と人の役目で、この継手は送る前と受け取った後の検めだけをします。",
          "どの連携を許すかを決めたいとき。その決定は核（policy-facts）の役目です。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "行き先・スキーマ・profile を検めます。pin した一覧に無いものは送りません。空の一覧は何も許しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "取り消せない書出しは、設定が allow_irreversible: true のときだけ送ります。そうでなければ送りません。",
          "段階と効果を照合します。作れた（GENERATED）なのに外への効果を言う receipt、届けた（DELIVERED）なのに効果の無い receipt は受理しません。GENERATED の効果は NONE か LOCAL_REVERSIBLE、DELIVERED・ACCEPTED は EXTERNAL_* です。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（DataExport）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には delivery_state・delivered・destination_accepted が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: 段階で異なります（NONE〜EXTERNAL_*）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-06",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "スキーマレジストリ — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、スキーマ登録（SchemaRegistration）の要求を、pin したスキーマの一覧で検めてから連携先へ送り、戻った receipt を契約どおりに照合する継手（つぎて）です。登録できたことを、Schema Authority や Canonical と取り違えません（fail-closed）。",
        "problem": "契約（型）を共有するためにスキーマを登録するとき、pin していないスキーマを登録してしまうこと、そして「登録できた」ことを「そのスキーマが権威（Schema Authority）や Canonical になった」と扱ってしまうことを防ぎます。",
        "use_when": [
          "契約（型）の共有のために、pin したスキーマだけを連携先に登録したいとき。",
          "登録の結果を、receipt の registration_state・schema_authority・canonical_status で照合してから扱いたいとき。",
          "登録の効果を EXTERNAL_REVERSIBLE、重複（DUPLICATE）と失敗（FAILED）を効果なし（NONE）として照合したいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "登録によって、そのスキーマを権威や Canonical にしたいとき。スキーマの登録は Schema Authority も Canonical も生みません。",
          "どのスキーマを権威とするか（Schema Authority）を決めたいとき。それは核と人の役目で、この継手の契約の権威も、まだ定まっていません（NOT_SPECIFIED）。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "スキーマを検めます。pin したスキーマの一覧に無いものは送りません。空の一覧は何も許しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "権威（Schema Authority）を名乗る登録の receipt、Canonical を名乗る登録の receipt は受理しません。",
          "digest の対象を照合します。契約の読みとして、schema_digest の対象は schema_identity、content_digest の対象は request_id です。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。登録の効果は EXTERNAL_REVERSIBLE（DUPLICATE・FAILED は NONE）です。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "スキーマの登録は、Schema Authority も Canonical も生みません。",
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（SchemaRegistration）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には registration_state・schema_authority・canonical_status が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: EXTERNAL_REVERSIBLE（DUPLICATE・FAILED は NONE）。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-07",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "イベントスキーマ進化 — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、スキーマ互換（SchemaCompatibility）の確認の要求を、pin したスキーマで検めてから連携先へ送り、戻った receipt の互換の主張と試験の結果（test_result）を契約どおりに照合する継手（つぎて）です。互換 UNKNOWN は互換として扱いません（fail-closed）。",
        "problem": "イベントのスキーマを変えるとき、互換が確かめられていない（UNKNOWN）結果や試験に落ちた結果を「互換あり」と扱ってしまい、後方互換を壊すことを防ぎます。",
        "use_when": [
          "後方互換の管理のために、スキーマ互換の確認の結果を照合してから扱いたいとき。",
          "互換の主張（BACKWARD・FORWARD・FULLY）と試験の PASS がそろったときだけ、互換（compatible: true）として扱いたいとき。",
          "pin したスキーマ（イベントのスキーマを含む）に限って、互換を確かめたいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "互換 UNKNOWN や、試験が未実行（NOT_RUN）の主張を、互換として扱いたいとき。この継手はそれを互換として扱いません。",
          "スキーマの意味の正しさや、どれを権威とするか（Schema Authority）を決めたいとき。それは核と人の役目です。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "スキーマを検めます。pin したスキーマの一覧に無いものは送りません。空の一覧は何も許しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "互換が UNKNOWN の receipt は、試験が PASS でも互換として受理しません。",
          "試験に落ちた（FAIL）のに互換（compatible: true）とする receipt、互換の主張（BACKWARD・FORWARD・FULLY）と FAIL が並ぶ矛盾した receipt は受理しません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。互換の確認の効果は NONE です。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（SchemaCompatibility）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には compatibility・test_result・compatible が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-08",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "外部 OAuth 接続管理 — 連携・ETL 継手（Tsugite）",
        "summary": "連携・ETL 継手（Tsugite）は、外部接続の登録（ExternalConnectionRegister）の要求を、pin した接続・credential の参照・scope の一覧で検めてから送り、戻った receipt を契約どおりに照合する継手（つぎて）です。実の OAuth の手続きや token の発行・保管はせず、接続できたことを業務の許可と取り違えません（fail-closed）。",
        "problem": "連携アカウントの接続を登録するとき、token や鍵の値を参照の代わりに渡してしまうこと、許可していない scope で登録してしまうこと、そして「接続できた」を「業務として許された」と扱ってしまうことを防ぎます。",
        "use_when": [
          "連携アカウントの接続を、pin した接続（connection_refs）・credential の参照（credential_refs）・許可する scope（allowed_scopes）の範囲に限って登録したいとき。",
          "credential を値（bytes）ではなく、参照だけで渡したいとき。",
          "登録の結果を、receipt の connection_state・credential_binding_state・business_authorization で照合してから扱いたいとき。",
          "同じ要求を二重に送りたくないとき。連携先の障害で結果が分からないときは UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "do_not_use_when": [
          "OAuth の手続きを行わせたいとき、token を発行・保管させたいとき。この継手はそれをしません。credential の参照が何を指すかの解決は secret-ref／portable-credential-ref の仕組みの役目です。",
          "接続できたことを、業務の許可として使いたいとき。どの連携を許すかの決定は核（policy-facts）の役目です。",
          "token や鍵の形の値を見分けて守る仕組みとして頼りたいとき。この検出は代表的な形だけを見る網で、すべての秘密を見分けるものではありません。本当の守りは pin した参照の一覧です。",
          "特定の SaaS 専用の接続器がほしいとき。この継手は SaaS ごとの接続器ではなく、どの連携先にも同じ検めをかけます。",
          "すぐに使える連携先がほしいとき。実の連携先は含まず、どこにつなぐかはまだ決まっていません。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "接続・credential の参照・scope を検めます。pin した一覧に無い接続や参照、許可していない scope は送りません。空の一覧は何も許しません。",
          "credential_ref が token や鍵の形なら、pin されていても送りません。",
          "接続できたことから業務の許可を導く receipt は受理しません。",
          "参照を検めます。空の参照、パスの形や「..」を含む参照、追加のフィールド、決まった値（const）と違う値は送りません。",
          "外への効果を増やす receipt、効果が UNKNOWN の receipt、別の要求の receipt（request_digest の偽り）を受理しません。受理できない receipt で停止します（HALTED）。接続の登録の効果は NONE か LOCAL_REVERSIBLE です。",
          "同じ request_id は二度送りません。同じ冪等キーで別の中身の要求は送りません（REFUSED）。連携先の障害は UNKNOWN とし、照会（lookup）で解きます。"
        ],
        "what_it_does_not_do": [
          "実の OAuth の手続き、token の発行と保管はしません。credential の参照が何を指すかの解決は secret-ref／portable-credential-ref の仕組みの役目です。",
          "token や鍵の形の検出は代表的な形だけを見る網で、すべての秘密を見分けるものではありません。",
          "意味の正しさ・Schema Authority・業務の許可を決めるのは、核（reconcile-facts・policy-facts）と人の役目です。この継手がするのは、送る前の検めと受け取った後の照合だけです。",
          "運べた・登録できた・接続できたことは承認ではありません（どの結果にも approval: false が付きます）。",
          "実の連携先を含みません。どの SaaS・どの ETL 基盤につなぐか、port の実装、pin する一覧は未定で、接続するときに別の関門で決めます。",
          "N-01 サービス発見・N-02 API ゲートウェイの機能は、この継手にはありません。N-01 は継手 service-registry-tsugite（契約 service-registry/v1）、N-02 は部品(Component) gateway-routing-component が担います。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/integration-etl/v1（ExternalConnectionRegister）でつなぎます。createIntegrationClient({ cfg, port }) の submit(request) は、DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（常に approval: false）。DONE には connection_state・credential_binding_state・business_authorization が付きます。利用側の用意: 連携先の port（submit と任意の lookup）と cfg の pin 一覧。副作用: NONE か LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-09",
        "category": "N/連携基盤",
        "kind": "Primitive",
        "title": "取得の記録 — 取得の記録（fetch.json）の部品（Primitive）",
        "summary": "取得の記録（fetch.json）の部品（Primitive）は、いつ・どこから・何を取得したかの事実だけを書いた記録 fetch.json（schema erabazu.raw.fetch/1）を作り、検める単独の部品です。中の fetch_record は契約 raw-fetch/v1 の RawFetchReceipt.fetch_record と同じキー・同じパターンです。記録は RFC 8785 で正準化し、その sha256 を返します。何も取得しません。",
        "problem": "取り込みの来歴の土台になります。受け取れない記録は黙って読み飛ばさずに決まったコードと位置で止め、事前署名 URL の痕跡（://・X-Amz-Signature・X-Amz-Credential・Signature=、クエリつきの URL の形）を記録のどこにも入れません。記録の hash は正準の bytes の sha256 で、キーの順や空白に左右されません。",
        "use_when": [
          "取得の事実（source・platform_profile・retention_class・kind_declared・fetch_state・fetch_record）から、fetch.json の記録を作りたいとき（buildFetchRecord）。schema と folder_stamp は部品が足します。",
          "契約 raw-fetch/v1 の受け取り（RawFetchReceipt）の fetch_record を、そのまま写して記録に入れたいとき。",
          "保管した記録を読み戻して検め、同じ canonical と record_sha256 を得たいとき（checkFetchRecord。folder_stamp は fetched_at から作り直して比べる）。",
          "has_more が true のまま止まった取得（PARTIAL）も、取れた分の記録として残したいとき。",
          "時計・乱数・外の接続を持たない、決定的な処理として手元で呼びたいとき。fetched_at は呼ぶ側が渡します。"
        ],
        "do_not_use_when": [
          "ファイルがあるか、byte_size と sha256 が中身と合うかを確かめたいとき。それは I-10 の役目で、この部品は検めません。",
          "記録に秘密が入っていないことを確かめたいとき。URL・署名の印の網は粗く、:// を含まない秘密（トークン・鍵の値そのもの）や、印を崩した URL は見つけられません。",
          "取得が本当に FETCHED・PARTIAL だったか、fetched_at が取得した時刻として正しいか・未来でないかを確かめたいとき。写しとして受け取り、形と暦だけを検めます。",
          "保持の方針（retention_class の意味・期間）を決めたいとき。ここでは決めません。",
          "大文字・小文字を区別しないファイルシステムで、name の重なりを見つけたいとき。a と A は別の名前として受け取ります。",
          "宣言された media type が中身に合うか・charset が正しいかを確かめたいとき。宣言のまま記録します（読むのは I-09・I-10）。",
          "契約 raw-fetch/v1 が改訂された後に、写し直さずに使うとき。パターンの文字列は契約の写しで、改訂されたら写し直して検め直す必要があります。Node.js v24.21.0 のほかの版も確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "記録（または build の入力）がオブジェクトでないとき NOT_AN_OBJECT、RFC 8785 で正準化できない値（小数・undefined・getter・Date など）を含むとき NOT_CANONICALIZABLE（detail に I-08 のコード）で止めます。getter は呼ばず、正準化した写しの上で検めます。",
          "決まっていないキーがあるとき（build に schema・folder_stamp を渡した場合を含む）UNKNOWN_FIELD、必須のキーが無いとき MISSING_FIELD で止めます。fetch_params_digest と files[].file_ref も必須です。",
          "schema・source・platform_profile・retention_class・kind_declared・fetch_state が決まった値や形でないとき、INVALID_SCHEMA・INVALID_SOURCE・INVALID_PLATFORM_PROFILE・INVALID_RETENTION_CLASS・INVALID_KIND_DECLARED・INVALID_FETCH_STATE で止めます。fetch_record を持たない状態（NOT_READY・FAILED など）は受け取りません。",
          "fetched_at が契約のパターン（1970 年以降・UTC の Z・うるう秒なし）に合わない、またはその月に無い日（2 月 30 日・平年の 2 月 29 日など）のとき INVALID_FETCHED_AT、folder_stamp が fetched_at から作り直した値と違うとき FOLDER_STAMP_MISMATCH で止めます。",
          "Digest の形（キー・algorithm・value・domain）が違うとき、files[].sha256.target が name と違うとき INVALID_DIGEST、files が空・キーが違う・byte_size が 0 以上の整数でないとき INVALID_FILES、name が契約のパターンでない（.. を含む）とき INVALID_FILE_NAME、同じ name が 2 つ以上のとき DUPLICATE_FILE_NAME、declared_media_type が契約の形でない・255 文字を超えるとき INVALID_MEDIA_TYPE で止めます。",
          "pages が空、seq が 0 から 1 ずつでない、cursor がつながらない、has_more が状態に合わない（FETCHED は最後だけ false、PARTIAL はすべて true）とき、INVALID_PAGES で止めます。",
          "platform_profile が amazon-sp-api-reports・amazon-pay-reports なのに lineage が無いとき LINEAGE_REQUIRED、stripe なのに api_version が null のとき API_VERSION_REQUIRED で止めます（FETCHED でも PARTIAL でも）。lineage の形が違うときは INVALID_LINEAGE です。",
          "記録のどこかの文字列がクエリつきの URL の形を含むとき URL_WITH_QUERY_REFUSED、://・X-Amz-Signature・X-Amz-Credential・Signature= を含むとき URL_FORM_REFUSED で止めます（大文字・小文字を区別せず、探しは線形の時間）。止めたときは、決まった順で最初の 1 つを code と path（JSON Pointer）で返します。"
        ],
        "what_it_does_not_do": [
          "Stripe（Amazon）は接続先として名前を挙げているだけで、提携・承認ではありません。",
          "何も取得しません。ファイルの読み書きもしません。",
          "記録を受け取れたことは、取得が正しかったこと・ファイルがあること・digest が中身と合うこと・秘密が入っていないこと・受け入れてよいことを意味しません。",
          "契約の runtime 規則のうち、要求（request）を持たないと確かめられないもの（fetch_params_digest の target・value が要求と合うか）や、記録の外のことは検めません。",
          "可読性の検め（I-09）、封（H-11）、保持の方針は、この部品の外に残ります。",
          "記録ができたことは、承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の取得ツールの記録形式の雛形ではありません。"
        ],
        "integration": "src/fetch-record.js の buildFetchRecord({ source, platform_profile, retention_class, kind_declared, fetch_state, fetch_record }) と checkFetchRecord(record) を呼びます（投げません）。check は schema と folder_stamp を含む 8 つのキーの記録を受け取ります。返すのは { ok: true, errors: [], record, canonical, record_sha256 } か { ok: false, errors: [{ code, path }] }。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。依存は Node.js の組み込みの crypto だけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-10",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "生データの取得（Stripe） — 生データの取得（Stripe）の継手（Tsugite）",
        "summary": "生データの取得（Stripe）の継手（Tsugite）は、Stripe の一覧の API から、頁ごとの応答の本文を受け取った形のまま取り、取得の記録（fetch_record）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。中身は読まず、正規化も判断もしません（fail-closed）。",
        "problem": "連携の取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。受け取ったバイト列の長さと sha256 を計算し直して記録と照らし、カーソルの連鎖・API の版・宣言を確かめ、事前署名の URL を残さず、取得を判断や受け入れと取り違えません。",
        "use_when": [
          "Stripe の一覧の API（charges・payment_intents・refunds・balance_transactions・payouts・disputes・invoices・events）の応答を、受け取った形のまま残したいとき。",
          "受け取ったバイト列の byte_size と sha256 が、取得の記録（fetch_record）と同じことを確かめてから受け取りたいとき。",
          "頁を途中で止めても、続きを取れるようにしたいとき。PARTIAL のときの result.resume_cursor を、次の要求の starting_after にします。",
          "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままです。"
        ],
        "do_not_use_when": [
          "応答の中身を読みたい・型にはめたい・正規化したいとき。共通形への写像・重複の判断・突き合わせは、ほかの層と人・ルールの役目です。",
          "レポートを作らせたいとき（CREATE_REPORT）。この継手が送るのは op FETCH だけで、ほかの profile も送りません。",
          "Stripe の API クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
          "保持の期限を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
          "すぐに使える取得の仕組みがほしいとき。実の Stripe からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "要求を検めます。送るのは profile stripe・op FETCH・limit 1〜100・starting_after と ending_before の一方だけの要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。外れたら送りません（REFUSED）。",
          "資格情報は参照だけです。用途 FETCH・スコープ READ_ONLY と書いた参照だけを使い、署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
          "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。",
          "pages を照合します。seq は 0 から 1 ずつの切れないカーソルの連鎖で、FETCHED は has_more false で終わり、PARTIAL は続きのカーソルを持って止まるものだけを受け取ります。FETCHED と PARTIAL には api_version の文字列が要ります。",
          "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
          "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。",
          "状態の食い違う receipt、効果を増やす receipt（FETCH はどの状態でも NONE）、効果の不明な receipt、別の要求の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。",
          "同じ request_id の別の中身は送りません。Stripe（port）の障害は UNKNOWN とし、読むだけなので同じ要求を送り直して構いません。"
        ],
        "what_it_does_not_do": [
          "Stripe は接続先として名前を挙げているだけで、提携・承認ではありません。",
          "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません。",
          "Stripe の一覧の API の実の振る舞い（並び順・ending_before の向き・has_more）は代役で真似ただけで、実の Stripe とは突き合わせていません。API の版の値の形も確かめません。",
          "時刻（observed_timestamp・fetched_at）は形と実在する日時だけを検め、正しさ・古さは確かめません。1 回で何頁取るか・時間の予算は port の役目です。",
          "照合済みの記録はこの継手の中だけで、再起動で消えます。fetch.json・payload の書き出しは N-09 と保管の役目です。",
          "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile stripe・op FETCH）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Stripe の port（submit が { receipt, files } を返す）と cfg（account_refs・credentials・signing_key_refs）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-11",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "生データの取得（Amazon SP-API のレポート） — 生データの取得（Amazon SP-API のレポート）の継手（Tsugite）",
        "summary": "生データの取得（Amazon SP-API のレポート）の継手（Tsugite）は、Amazon SP-API の Reports から、レポートの文書（TSV の gzip など）を受け取った形のまま取り、取得の記録（fetch_record と lineage）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。中身は読まず、展開も正規化も判断もしません（fail-closed）。",
        "problem": "レポートの取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。文書を gzip のまま運び、長さと sha256・lineage・宣言を照らし、事前署名の URL を残さず、作らせたレポートを二度作らせません。",
        "use_when": [
          "自動で作られるレポート（決済報告書）を、REPORT_PROCESSING_FINISHED の通知（別の port）の後に report_id で取りたいとき（op FETCH）。",
          "オンデマンドのレポートを作らせて report_id を受け取りたいとき（op CREATE_REPORT）。作らせてよい種類は設定 allowed_report_types に書いたものだけです。",
          "受け取った文書の byte_size と sha256 が取得の記録と同じで、GZIP の文書が展開されていないことを確かめてから受け取りたいとき。",
          "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままで、lineage を含みます。"
        ],
        "do_not_use_when": [
          "文書を展開したい・TSV を読みたい・共通形へ写したいとき。それはほかの層の役目で、判断は人・ルールの役目です。",
          "自動で作られる決済報告書（GET_V2_SETTLEMENT_REPORT_…）を作らせたいとき。その種類は設定に入れられません。",
          "REPORT_PROCESSING_FINISHED の通知を受けたいとき。通知の受け口は別の port です。",
          "SP-API の Reports クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
          "保持の期限（顧客の PII の期限など）を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
          "すぐに使える取得の仕組みがほしいとき。実の SP-API からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "要求を検めます。送るのは profile amazon-sp-api-reports の要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。許していないレポートの種類、同じ request_id の別の中身は送りません（REFUSED）。",
          "資格情報は参照だけです。用途 FETCH・スコープ REPORTS_ONLY と書いた参照だけを使い、署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
          "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。GZIP の文書は application/gzip と宣言し、gzip の頭（1f 8b）で始まるときだけ受け取ります。",
          "lineage を照合します。FETCHED と PARTIAL には lineage が要り、FETCH の lineage.report_id は要求の report_id と同じでなければ受け取りません。lineage に文書の URL は書かせません。",
          "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
          "効果を照合します。REPORT_REQUESTED は EXTERNAL_REVERSIBLE をちょうど名乗るものだけを受け取り、ほかの状態は NONE だけです。レポートが作られたか分からない receipt は受け取らず、停止します。",
          "結果の分からない CREATE_REPORT は未解決として覚え、同じ要求の再送で解きます（port.lookup があれば先に探します）。レポートを二度作らせず、解けるまでほかの要求は送りません。",
          "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。照合できない receipt のあとは停止します（RECEIPT_REJECTED のあとは HALTED）。"
        ],
        "what_it_does_not_do": [
          "Amazon は接続先として名前を挙げているだけで、提携・承認ではありません。",
          "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません（GZIP のときの gzip の頭 2 バイトだけは見ます）。",
          "SP-API の実の振る舞い（レポートの処理時間・CANCELLED や FATAL の扱い・文書の URL の寿命・GZIP の有無）は代役で真似ただけで、実の SP-API とは突き合わせていません。",
          "未解決の CREATE_REPORT はこの継手の中だけで覚え、再起動で消えます。lookup の無い port で送り直すと、レポートがもう 1 つ作られることがあります。",
          "どの種類をオンデマンドで作れるかは、設定 allowed_report_types に任せます。決済報告書のほかに自動で作られる種類は知りません。",
          "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile amazon-sp-api-reports）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: SP-API の port（submit と任意の lookup。{ receipt, files } を返す）と cfg の一覧。副作用: CREATE_REPORT だけ EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "N-12",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "生データの取得（Amazon Pay のレポート） — 生データの取得（Amazon Pay のレポート）の継手（Tsugite）",
        "summary": "生データの取得（Amazon Pay のレポート）の継手（Tsugite）は、Amazon Pay の Reports API から、決済ごとに自動で作られる決済報告書を、通知の後に report_id で受け取った形のまま取り、取得の記録（fetch_record と lineage）と一緒に返す継手（つぎて）です。要求（RawFetchRequest）を検めてから port へ運び、receipt（RawFetchReceipt）と受け取ったバイト列を照合します。レポートは作らせません（fail-closed）。",
        "problem": "決済報告の取り込みの裏で、受け取ったままかが見えなくなることを防ぎます。決済報告書を受け取った形のまま運び、長さと sha256・lineage・宣言を照らし、短命のダウンロードの URL を残さず、取得を判断や受け入れと取り違えません。",
        "use_when": [
          "決済報告書の通知（別の port）を受けた後に、その report_id で決済報告書を取りたいとき（op FETCH）。処理中は NOT_READY、無いレポートは REJECTED が返ります。",
          "受け取った決済報告書の byte_size と sha256 が取得の記録と同じことを確かめてから受け取りたいとき。",
          "ダウンロードの URL（30 秒で切れる）を記録に残さずに取りたいとき。URL は port がすぐに使って捨て、継手には渡しません。",
          "取得の記録を、N-09 の取得の記録（fetch.json）へそのまま写したいとき。result.fetch_record は receipt のままで、lineage を含みます。"
        ],
        "do_not_use_when": [
          "レポートを作らせたい・予約したいとき。決済報告書は自動で作られ、この継手は CREATE_REPORT を送りません（契約で無効）。",
          "CSV を読みたい・共通形へ写したいとき。それはほかの層の役目で、判断は人・ルールの役目です。",
          "決済報告書の通知を受けたいとき。通知の受け口は別の port です。",
          "Amazon Pay の Reports クライアントの雛形がほしいとき。この継手はその雛形ではありません。",
          "保持の期限を守らせたいとき。保持を守るのは保管（C-01）とその区画の役目です。",
          "すぐに使える取得の仕組みがほしいとき。実の Amazon Pay からの取得は 0 回で、port の実装・資格情報・設定の値は、接続するときに別の関門で決めます。"
        ],
        "what_it_checks": [
          "要求を検めます。送るのは profile amazon-pay-reports・op FETCH・report_id だけの要求で、口座（account_refs）と資格情報（credentials）が設定に pin されているときだけです。CREATE_REPORT、ほかの profile、同じ request_id の別の中身は送りません（REFUSED）。",
          "資格情報は参照だけです。用途 FETCH と、スコープ READ_ONLY か NO_READ_ONLY_SCOPE_OFFERED（相手が読み取りだけのスコープを持たないと明示した）を書いた参照だけを使います。記録の署名の鍵（signing_key_refs）と同じ参照を credentials に書いた設定は受けません。",
          "受け取ったバイト列を写してから照合します。files[] と同じ順・名前・数で、byte_size が実のバイト数、sha256 が実のバイト列の sha256 のときだけ受け取ります。GZIP と書かれたときは、application/gzip と宣言し gzip の頭（1f 8b）で始まるときだけ受け取ります。",
          "lineage を照合します。FETCHED と PARTIAL には lineage が要り、FETCH の lineage.report_id は要求の report_id と同じでなければ受け取りません。",
          "事前署名の URL を記録しません。presigned_url_recorded は false で、要求と receipt のどの値にも ://・X-Amz-Signature・X-Amz-Credential・Signature= を（大文字小文字を問わず）許しません。",
          "宣言（kind_declared・retention_class・identity_basis）が要求と同じ値の receipt だけを受け取ります。",
          "状態の食い違う receipt、効果を増やす receipt（FETCH はどの状態でも NONE）、効果の不明な receipt、別の要求の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。",
          "Amazon Pay（port）の障害は UNKNOWN とします。読むだけなので、同じ要求を送り直して構いません。"
        ],
        "what_it_does_not_do": [
          "Amazon は接続先として名前を挙げているだけで、提携・承認ではありません。",
          "受け取ったバイト列の中身（形・件数・正しさ・PII を含むか）は確かめません。宣言が中身に合うか、declared_media_type が中身の型に合うかも確かめません（GZIP と書かれたときの gzip の頭 2 バイトだけは見ます）。",
          "Reports API の実の振る舞い（レポートの処理・ダウンロードの URL の寿命・compressionAlgorithm の値）は代役で真似ただけで、実の Amazon Pay とは突き合わせていません。",
          "Amazon Pay の資格情報が読み取りだけのスコープを持てるかは確かめていません。実の鍵の用途は port が確かめます。",
          "照合済みの記録はこの継手の中だけで、再起動で消えます。fetch.json・payload の書き出しは N-09 と保管の役目です。時刻は形と実在する日時だけを検め、正しさ・古さは確かめません。",
          "取得できたことは承認ではなく、中身が正しいことでも、受け入れたことでも、突き合わせたことでもありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/raw-fetch/v1（RawFetchRequest / RawFetchReceipt、profile amazon-pay-reports・op FETCH）でつなぎます。createRawFetchClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Amazon Pay の port（submit が { receipt, files } を返す）と cfg（account_refs・credentials・signing_key_refs）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "O-01",
        "category": "O/検索・索引・取り出し",
        "kind": "Tsugite",
        "title": "索引・検索・取り出し — 索引・検索・取り出しの継手（Tsugite）",
        "summary": "索引・検索・取り出しの継手（Tsugite）は、差し替えられる索引の基盤へ、索引の書込（UPSERT・墓標の UPSERT・DELETE）、検索（KEYWORD・VECTOR・HYBRID・STRUCTURED）、取り出し（source の digest と出所）の要求を、契約と設定で検めてから port へ運び、返った receipt を照合する継手（つぎて）です。見つかったものを事実とはしません（fail-closed）。",
        "problem": "「検索で出た」の裏で、索引の版や鮮度が見えなくなることを防ぎます。設定で狭めて運び、問い合わせの文書の digest と cursor の決定性を照らし、引いた結果を確かめた事実や権威と取り違えません。",
        "use_when": [
          "索引の基盤（既定は利用者の SQLite・FTS5 の部品。差し替えられる）に、索引の書込・検索・取り出しを、同じ検めをかけて運びたいとき。",
          "完全一致の絞り込み（STRUCTURED）で引きたいとき。問い合わせの文書 canonical({filters:[{field, op, value}]}) の sha256 を query_digest と照らしてから渡します。",
          "ページを続けて引くとき、同じ索引・版・問い合わせの ANSWERED の答えで受け取った cursor だけを使いたいとき。",
          "個人情報を消すとき、索引の項目を墓標（tombstone true、content_digest なしの UPSERT）で上書きしたいとき。"
        ],
        "do_not_use_when": [
          "引いた結果を、確かめた事実や権威として使いたいとき。取り出した ≠ 真、似ている ≠ 確かめた事実、順位 ≠ 権威です。結果の確かめ・判断は外の判断と人・組織の役目です。",
          "取り出した中身のバイト列を受け取りたいとき。この継手を通るのは digest と出所だけで、中身が digest と合うかは受け取る側で確かめます。",
          "特定の検索エンジン・ベクトル DB・SQLite の拡張の使い方の雛形がほしいとき。この継手はその雛形ではありません。",
          "Canonical に定まった分類・契約の上で使いたいとき。分類 O と契約 search-retrieval/v1 は DESIGN_CANDIDATE で、この継手もその上の候補です。",
          "すぐに使える索引の基盤がほしいとき。実の基盤への呼び出しは 0 回で、port の実装・設定の値は、接続するときに別の関門で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定は狭めるだけで広げません。index_profiles・index_ops・retrieval_modes・max_page_size・structured_fields・source_prefixes の外の要求は送りません（DELETE を外せば DELETE は送りません）。設定に知らないキーがあれば何も送りません。",
          "STRUCTURED の文書を検めます。形（filters は 1 つ以上、op は EQ・GTE・LT、value は文字列、field は設定の structured_fields）と sha256 が query_digest と合わないときは送りません。結果は score 1 だけ受け取ります。",
          "cursor を検めます。別の index_revision・別の問い合わせの cursor、受け取っていない cursor は送りません。決定的でない cursor、最初の頁と続きの頁で同じ cursor、page_size を超える結果、同じ source の重複は受理しません。",
          "検索の答えを照合します。ANSWERED でない答えは結果も cursor も持たず、鮮度は UNKNOWN のときだけ受け取ります。結果の項目は {source_ref, score, provenance_ref} だけで、verified などの欄は受け取りません。",
          "効果を照合します。INDEXED・DELETED は EXTERNAL_REVERSIBLE、検索・取り出しは NONE、UNKNOWN は索引の書込の FAILED だけです。効果を増やす receipt、別の版や op、別の要求の receipt は受理せず、停止します（HALTED）。",
          "結果の見えない索引の書込（port の障害、FAILED・効果 UNKNOWN の receipt）は未解決として保ちます。送り直す前に、索引の項目を EntryReadRequest / EntryReadReceipt（port.readEntry）で読み、効いていれば送り直しません。読めないときは送り直さず、解けるまでほかは送りません。"
        ],
        "what_it_does_not_do": [
          "ACL を結果を返す前に当てたか、鮮度（freshness_state）が本当か、結果が本当に一致・関連しているかは確かめません（基盤の申告。結果は NOT_VERIFIED のまま運びます）。",
          "STRUCTURED で基盤が値で比べたか、絞り込みの値が宣言した型に合うかは確かめません（基盤が FILTER_INVALID で答えます）。",
          "VECTOR・HYBRID の意味（どの埋め込みか・どう混ぜるか）は契約に無く、score を確からしさとして運ぶだけです。ベクトル検索は基盤の必須にはしません。",
          "cursor の照合と記録はこのクライアントの中だけで、再起動で消えます。再起動の前の cursor ではページを続けられず、最初のページから引き直します。",
          "readEntry の答えが本当に索引の今の項目かは確かめません。readEntry の無い port では、FAILED・UNKNOWN の後は送り直さず、索引に何が残ったかは人が確かめます。",
          "doc_ref・acl_ref・provenance_ref の文字列に個人情報が書かれても見分けません。個人情報そのものを索引に入れないことは運用の決まりです。",
          "運べた・索引に入れたことは承認ではなく、中身を確かめたことでもありません（どの結果にも approval: false が付きます）。契約を変えず、契約の権威は NOT_SPECIFIED です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/search-retrieval/v1（Index・Query・Retrieve・EntryRead の Request と Receipt）でつなぎます。createSearchRetrievalClient({ cfg, port, clock? }) の submit(request, query) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: 索引の基盤の port（submit と任意の lookup・readEntry）と cfg の一覧。副作用: 索引の書込は EXTERNAL_REVERSIBLE、検索・取り出しは NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "O-02",
        "category": "O/検索・索引・取り出し",
        "kind": "Component",
        "title": "取得・取引の索引 — 取得・取引の索引（fetch.db・txn.db）の部品（Component）",
        "summary": "取得・取引の索引の部品（Component）は、索引・検索の継手（O-01）・記録の検め（部品(Primitive) N-09）・取引の事象 ID（部品(Kernel) G-12）を束ね、2 つの索引を作る部品です。fetch の索引にはすべての raw の取得を、txn の索引には取引年月日・取引金額・取引先と raw の参照を、O-01 の UPSERT で入れます。fetch.json は N-09 で、txn_id は G-12 で検め、食い違えば入れません。問い合わせは STRUCTURED で、結果は txn_id の候補です。DELETE は送りません。索引は探す助けで、結果は事実ではありません。",
        "problem": "帳簿の検索の裏にある索引のための部品です。検めていない記録や txn_id の合わない取引を索引に入れること、pii の取引先をそのまま追記だけの索引に入れること、索引の項目を DELETE で消してしまうこと、索引が答えなかった問い合わせを「結果の無い成功」と読むことを防ぎます。",
        "use_when": [
          "fetch.json の記録を N-09 で検め、取得（source・folder_stamp・fetched_at・retention_class・検査の状態と版・ファイルの数）を fetch の索引へ UPSERT で 1 つ入れたいとき（indexFetch）。doc_ref は raw/<source>/<folder_stamp> です。",
          "共通形の取引（erabazu.common.txn/1）の txn_id を G-12 で計算し直し、合った取引だけを txn の索引へ入れたいとき（indexTxn）。文書は occurred_on・amount_key・currency・counterparty_key・raw_refs で、doc_ref は txn_id です。",
          "取引を、取引年月日（EQ・GTE・LT）・金額（amount_key の EQ・GTE・LT。同じ通貨の EQ と組む）・通貨と取引先（EQ だけ）で探し、txn_id の候補を受け取りたいとき（queryTxn）。",
          "金額を、文字列の順が数の順と同じになる 58 文字の固定幅の amount_key に、取引先を counterparty_key（\"id:\" か \"name:\" で始まる値。pii は sha256）にして、問い合わせの値を作りたいとき（amountKey・counterpartyKey）。",
          "UPSERT だけの索引から pii の取引を取り除きたいとき。txn の索引の項目を墓標（tombstone true・content_digest なしの UPSERT）で上書きします（tombstoneTxn）。墓標にした取引は、このインスタンスでは入れ直しません。",
          "結果の分からなかった索引の書込（墓標を含む）を、同じ要求で送り直したいとき（resolvePending）。"
        ],
        "do_not_use_when": [
          "問い合わせの結果を、条件に合う取引の事実や、漏れの無い一覧として使いたいとき。結果は候補（NOT_VERIFIED）で、当たった取引が本当に条件に合うか、索引に漏れが無いかは基盤の申告です。",
          "pii の取引先の sha256 を匿名化として扱いたいとき。sha256 は塩（秘密の鍵）を入れていないので、候補の名前や id を順に試せば当てられます（仮名化で、匿名化ではありません）。日付・金額・通貨・raw の参照は pii の取引でもそのまま索引に入ります。",
          "通貨をまたいで金額を比べたいとき。amount_key の絞り込みは同じ通貨の中だけで意味があり、為替の換算はしません。",
          "全文検索をしたいとき。全文検索（FTS5）は基盤の側で、この部品は構造化した絞り込みだけを送ります。fetch の索引への問い合わせの操作は v1 にありません。",
          "墓標で、保管にある raw まで消したいとき。墓標は索引の項目だけを上書きします。保管が raw を本当に消したかは確かめません。fetch の索引の項目には墓標の操作がありません。",
          "再起動の後やほかのプロセスでも、墓標にした取引の入れ直しを止めたいとき。墓標にした txn_id の控えはこのインスタンスの中だけです。控えを保つ場所は呼び出し側の運用です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が足りない・形が違う・2 つの profile が同じとき INVALID_OPTIONS、入力の形が違うとき INVALID_INPUT、取引の記録が索引に使える形でないとき INVALID_TXN、問い合わせの形が違うとき（amount_key に currency の EQ が無い など）INVALID_QUERY で止めます（where にその位置）。",
          "N-09 が fetch.json を受け取らないとき FETCH_RECORD_REFUSED、N-09 が投げた・答えが食い違うとき CHECK_PORT_FAILED で何も送りません。同じ folder_ref に別の fetch.json、同じ probe_version に別の文書のとき FETCH_INDEX_CONFLICT で送りません（直しは新しい probe_version）。",
          "G-12 が入力を受け取らないとき TXN_ID_REFUSED、G-12 が投げた・答えが食い違うとき TXN_ID_PORT_FAILED、計算し直した txn_id が記録と違うとき TXN_ID_MISMATCH（recomputed_txn_id を添える）で送りません。G-12 には 5 つの事実の写しだけを渡します。",
          "pii として送った txn_id を、ほかの区画で送る・墓標にするとき TXN_CLASS_CONFLICT、墓標にした txn_id を入れ直そうとするとき TXN_TOMBSTONED で送りません。",
          "索引の書込の結果が分からないとき INDEX_UNKNOWN で保留にし、保留の間は INDEX_PENDING で操作を受けません。継手が送らなかったとき INDEX_REFUSED、索引に入らなかった・答えが照合できない・別の doc_ref・別の op・別の index_revision・DELETED のとき INDEX_FAILED です。",
          "問い合わせの結果が分からないとき QUERY_UNKNOWN、送らなかったとき QUERY_REFUSED、結果の検め（score 1・NOT_VERIFIED・送った版・txn_id の形と重なり）に合わない・墓標にした txn_id が答えにあるとき QUERY_FAILED です。索引が答えなかったとき（query_state が ANSWERED でない）は QUERY_NOT_ANSWERED と query_state を返します。",
          "amountKey で金額が形・範囲（38 桁・scale 0〜18）の外のとき INVALID_AMOUNT、counterpartyKey で取引先の形が違うとき INVALID_COUNTERPARTY、区画が 3 つのどれでもないとき INVALID_RETENTION_CLASS です。timestamp が実在する日時を返さないとき CLOCK_FAILED で何も送らず、前の操作が終わっていないとき BUSY です。"
        ],
        "what_it_does_not_do": [
          "取得・取引の索引の部品は継手ではありません。自分の Request/Receipt は無く、索引の書込と問い合わせは、束ねた O-01 の継手が自分の契約（search-retrieval/v1）で行います（問い合わせは NONE）。この部品自身は外へ触れません。",
          "束ねた部品の意味（索引に入れた ≠ 中身を確かめた・似ている ≠ 事実・記録を受け取れた ≠ 取得の正しさ・ID が合う ≠ 取引が実在する）は、それぞれの部品の側に残ります。索引の基盤（SQLite・FTS5）と全文検索は基盤の側です。",
          "結果は事実でも承認でもありません（approval: false・authority: NOT_DERIVED、result_verification: NOT_VERIFIED、derives_authority: false）。",
          "DELETE は送りません。送るのは UPSERT の IndexRequest と STRUCTURED の QueryRequest だけです。",
          "取引の記録は、索引に使う欄とキーの集合だけを検めます。明細などの中身の検めは G-13 の役目です。retention_class は呼び出し側が渡す値をそのまま使い、raw の区画と合っているかは確かめません。文書の中身を基盤へ渡すのは、呼び出し側と基盤の結線の役目です。",
          "本物の O-01・N-09・G-12 には、この部品の試験では接続していません。単体試験とファズは代役です。結線の検めは通りましたが、墓標・query_state などの結線はその検めにありません。実の索引の基盤には接続していません。状態はプロセスの中だけで、再起動で消えます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の検索エンジン・データベースの雛形ではありません。"
        ],
        "integration": "src/ledger-index.js の createLedgerIndex(options) で作り、{ ok, errors, instance } を返します。options は 2 つの索引の profile と revision・request_prefix・ports・任意の timestamp です。操作は indexFetch・indexTxn・tombstoneTxn・queryTxn・resolvePending・status・amountKey・counterpartyKey で、結果に approval: false が付きます。port は index.submit（O-01 の継手のクライアント）・checkFetchRecord（N-09）・computeTxnId（G-12）です。txn の索引の項目の型は TXN_FIELD_TYPES で宣言します。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-01",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "契約・入力の検査 — 契約・入力検査の部品（Kernel）",
        "summary": "契約・入力検査の部品（Kernel）は、スキーマの id と digest・スキーマ・値（JSON の文字列か読み取り済みの値）・能力の宣言から、値がスキーマに合うか（conforms）を事実として出す単独の部品です。スキーマは決まった一部のキーワードだけを扱い、ほかのキーワードを含むスキーマや digest の合わないスキーマは信用せず、適合扱いしません。issue は位置（JSON Pointer）つきで並べます。inference と advice はいつも null です。",
        "problem": "入力の形の検めは、画面の裏で必ず要ります。どのスキーマで検めたか分からないまま適合扱いすること、JSON の重複キーを見落とすこと、pattern の正規表現の破滅的なバックトラック（ReDoS）で処理が止まらなくなることを防ぎます。",
        "use_when": [
          "値が、呼び出し側の渡すスキーマ（type・properties・required・additionalProperties: false・enum・const・items・minItems/maxItems・minLength/maxLength・minimum/maximum・pattern）に合うかを、事実として出したいとき（checkContract）。",
          "スキーマの同一性を確かめてから検めたいとき。schema_ref.digest が \"sha256:\" と RFC 8785 の正準形の SHA-256 の 16 進と一致しなければ、値を検めず SCHEMA_UNKNOWN とし、conforms は false です。digest だけは schemaDigest({ schema }) で出せます。",
          "JSON の文字列（value_text、UTF-8 で 1 MiB まで）を自前の厳密な読み取りで読み、重複キーを DUPLICATE_KEY として位置つきで受け取りたいとき。重複は JSON.parse と同じく後の値で検めます。",
          "足りない欄（missing）と、必要な能力のうち宣言に無いもの（capabilities_missing。capabilities_required から capabilities_declared を引いた集合）も、同じ呼び出しで受け取りたいとき。",
          "未入力を空として扱わず、need_input にしたいとき。digest が無いとき、capabilities_required があって capabilities_declared が無いときは、state が need_input です。",
          "issue を決まった順で 1000 件まで受け取り、切ったことを issues_total と issues_truncated で知りたいとき。",
          "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。キーの並びに関係なく、同じスキーマからは同じ digest ができます。"
        ],
        "do_not_use_when": [
          "完全な JSON Schema の検証が要るとき。参照のキーワード ref・allOf/anyOf/oneOf・format・if/then・null の型・型の配列・タプルの items・数の小数の範囲などは扱わず、UNSUPPORTED_KEYWORD か INVALID_SCHEMA になります。",
          "能力の宣言が本物か（権限の真正性）を確かめさせたいとき。この部品は宣言の集合の差を出すだけで、真正性は Host が確かめます。",
          "pattern に後方参照・後読み・先読み・入れ子の量指定子・\\p・大文字と小文字を区別しない照合・m フラグ・s フラグを使いたいとき。扱わない形は SCHEMA_PATTERN_UNSAFE になり、適合しません。",
          "値が業務として正しいか（スキーマの意味）を判断させたいとき。",
          "読み取り済みの値（value）で重複キーを確かめたいとき。value では重複キーが見えず、duplicate_keys_checked は false です。整数でない数は受け取りますが、どの型・どの enum とも一致しません。",
          "I-01 の入力スキーマ検証の部品と同じ結果を期待するとき。キーワードの範囲も結果の形も違い、互換ではありません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数の形が違うとき（知らないキー・getter・schema_ref か schema が無い・value_text と value の両方がある／どちらも無い・schema_ref の形の誤り）INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。",
          "知っているキーワードの形が違うとき（type が 6 つの名前でない・required の重複・空の enum・items がスキーマでない・負の件数・pattern が u フラグの正規表現として読めないなど）INVALID_SCHEMA で止めます。知らないキーワードと additionalProperties の false 以外の値は UNSUPPORTED_KEYWORD の issue にし、スキーマを信用しません。",
          "pattern は 128 文字・命令 4,096 個・{n,m} の 1000 までで、後方参照・後読み・入れ子の量指定子は SCHEMA_PATTERN_UNSAFE にします。照合は自前の線形時間の照合器（Pike VM）で、手間の合計を先に数え、50,000,000 を超えるときは照合を始める前に RESOURCE_LIMIT で止めます。",
          "digest が無いときは need_input、違うときは done で、どちらも SCHEMA_UNKNOWN の issue とし、適合しません。書き方は \"sha256:\" と小文字の 16 進だけで、大文字や接頭辞なしは一致しません。capabilities_declared が無いときも need_input です。",
          "value_text が JSON（RFC 8259）として読めないとき（BOM・先頭の 0・制御文字・誤ったエスケープを含む）INVALID_JSON、単独のサロゲートは LONE_SURROGATE で、文字の位置 offset つきで止めます。入れ子 64 段・1 MiB・値の個数の上限を超えるときは RESOURCE_LIMIT です。",
          "value が素の JSON でないとき（undefined・関数・BigInt・NaN・Infinity など）INVALID_VALUE、能力の一覧が 1〜128 文字の重複のない文字列の配列（256 個まで）でないとき INVALID_CAPABILITIES で止めます。\"__proto__\" も普通のキーとして扱い、原型を書き換えません。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。pattern の照合は、オラクル側の Node.js の RegExp（u フラグ）と突き合わせています。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "結果は、スキーマの決まった一部に値が合うかの事実です。値の意味の正しさ・利用の許可・権限の真正性を示すものではなく、データの検証サービスや権限の判定でもありません。",
          "pattern の照合の意味は、Node.js v24.21.0 の RegExp（u フラグ）と、生成した pattern と文字列で突き合わせたものです。ECMAScript の正規表現の全体を証明したものではありません。",
          "照合の手間の上限（pattern_steps）と value の値の個数の上限（value_nodes）は、単体試験だけで確かめました（ファズでは生成していません）。",
          "業務ごとの規則は持ちません。スキーマは呼び出し側が渡します。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/contract-check.js の checkContract({ schema_ref, schema, value_text | value, capabilities_declared?, capabilities_required? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（state・conforms・schema_trusted・issues・missing など、inference: null・advice: null）か { ok: false, errors: [{ code, path, ... }] } です。schemaDigest({ schema }) は digest だけを出します。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-02",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "対応付け・変換・照合 — 対応付け・変換・照合の部品（Kernel）",
        "summary": "対応付け・変換・照合の部品（Kernel）は、元のスキーマのレコードを対応表（元の位置 → 先の位置と変換）で先の形に写し、件数・キー・合計を照合する単独の部品です。当てるのは確認済み（confirmed）で AI 由来（ai_candidate）でない対応だけで、候補は候補のまま並べます。競合する対応は当てず、欠損・未知・0・空を分けて数えます。変換の結果は候補で、確定ではありません。inference と advice はいつも null です。",
        "problem": "移行や連携のデータの写し替えで、確認していない対応や AI の案を事実として当てること、表に無い値を推測で埋めること、意味の違う 2 つの値（入金済と予約枠確保など）を 1 つにまとめてしまうこと、浮動小数点や丸めで合計が狂うことを防ぎます。",
        "use_when": [
          "確認済みの対応だけでレコードを先の形に写したいとき（reconcileMap）。status が candidate の対応と origin が ai_candidate の対応は当てず、candidates_not_applied に理由（STATUS_CANDIDATE・ORIGIN_AI_CANDIDATE）つきで並べます。",
          "変換を 4 つ（copy の写し・enum_map の表による置き換え・unit の 10 進の正確な掛け算・decimal_scale の小数の桁そろえ）に限って使いたいとき。丸めはせず、rounding は \"none\" だけです。",
          "件数・キー・合計を照合したいとき。合計は sum_paths の位置の 10 進の文字列と整数を BigInt で正確に足し、元・写した先・expected を比べた結果と、足せなかった値の数を一緒に出します。",
          "欠損（missing_source_values）・未知（unknown_values。{ \"unknown\": true } のまま写す）・0・空を分けて数えたいとき。0・空の文字列・空の配列は値として写します。",
          "未対応の欄・表に無い値・変換できない値・キーの重複と欠けを、隠さず一覧で受け取りたいとき（unmapped_fields・unmapped_values・transform_failures・duplicate_keys・missing_keys）。",
          "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。外の境界・状態はありません。"
        ],
        "do_not_use_when": [
          "対応の意味が正しいか（同名異義を本当に見分けたか）を判断させたいとき。SAME_NAME_DIFFERENT_TARGET は同じ元の位置から違う先の位置への対応を並べるだけで、違う名前の同じ意味は見つけません。",
          "確認（status: confirmed・origin）が本物かを確かめさせたいとき。呼び出し側の記録によります。AI の案を人が採用したときは、呼び出し側が origin を human か registered にして渡します。",
          "配列の中を添字でたどる位置や、配列の要素ごとの変換が要るとき。v1 は配列をまるごと写すか、未対応の葉として数えます。",
          "単位の体系（次元・換算の正しさ）を確かめたいとき。from と to の名前の文字列を比べるだけで、factor の正しさは呼び出し側が持ちます。",
          "丸めが要るとき。decimal_scale で 0 でない桁を捨てる必要があれば、INEXACT_SCALE として transform_failures に数えます。",
          "変換の結果を適用・保存したいとき。この部品は写した候補を出すだけで、原子的な適用はこの部品の外の役目です。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数の形が違うとき（知らないキー・必要なキーが無い・id と版が 1〜256 文字でない・sum_paths の重複）INVALID_ARGUMENT、値を調べること自体が例外になるとき INSPECTION_FAILED、JSON Pointer の形でない（16 段・1024 文字を超える・単独のサロゲート）とき INVALID_PATH で止めます。",
          "対応表の形が違うとき INVALID_MAP、transform の kind が 4 つ以外か rounding が \"none\" 以外のとき UNSUPPORTED_TRANSFORM で止めます（丸めを暗黙にしません）。",
          "レコードが素の JSON のオブジェクトでないとき（null・整数でない数・単独のサロゲート・{ \"unknown\": true } に似た形）INVALID_RECORDS、expected の形が違うとき INVALID_EXPECTED、上限を超えたとき RESOURCE_LIMIT（limit つき）で止めます。\"__proto__\" のキーで原型を書き換えません。",
          "当てる候補の対応の中で、同じ先に 2 つ（TWO_SOURCES_ONE_TARGET）・先の位置の入れ子（TARGET_PATH_OVERLAP）・同じ元から違う先（SAME_NAME_DIFFERENT_TARGET）・同じ元の unit の from の違い（UNIT_MISMATCH）を競合として並べ、当てません。",
          "1 つの enum_map の表で 2 つ以上の元の値が同じ先の値になるときは MERGED_ENUM_VALUES として並べ、merge_confirmed: true が無ければ当てません。",
          "enum の表に無い値は推測で埋めず unmapped_values に、10 進でない値（NOT_A_DECIMAL）と桁を捨てる値（INEXACT_SCALE）は transform_failures に数え、どちらも converted のその欄を作りません。key_path を読む対応が当たっていなければ state は need_input（KEY_NOT_MAPPED）です。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "結果は、対応表とレコードから機械的に写した候補と照合の事実です。対応の意味の正しさ・データの移行の採用・適用の許可ではありません。採用と適用は Host・Runtime と人が行います。",
          "AI 由来の対応（ai_candidate）を事実にしません。converted は候補で、確定・適用ではありません。",
          "業務ごとの意味の対応表は持ちません。対応表は呼び出し側が渡します。",
          "レコードの値の個数の上限（record_nodes）は単体試験とファズのまれな生成だけで確かめ、expected.keys の上限（100,000 個）は試験していません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/map-reconcile.js の reconcileMap({ source_schema_id, target_schema_id, source_version, map, records, key_path, sum_paths?, expected? }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（converted・candidates_not_applied・conflicts・reconciliation・need_input など、inference: null・advice: null）か { ok: false, errors: [{ code, path[, limit] }] } です。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-03",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "差分・逆操作・競合の検査 — 差分・逆操作・競合検査の部品（Kernel）",
        "summary": "差分・逆操作・競合検査の部品（Kernel）は、JSON の文書について、2 つの版の差分（old つきの patch）、patch の逆、patch を当てた候補、目的（intent）ごとの取り消しの計画を出す単独の部品です。当てる前に基底の版の一致・保護された位置・前提の値を検め、取り消しでは後の別の変更との重なりと逆にできない変更を並べて隠しません。確定・保存はしません。inference と advice はいつも null です。",
        "problem": "「元に戻す」の裏の差分で、古い版に patch を当ててしまうこと、保護された位置を書き換えること、取り消しで後の別の変更を壊すこと、逆にできない変更を一部だけ逆にして済ませることを防ぎます。",
        "use_when": [
          "2 つの版の JSON の差分を、old つきの patch（add・remove・replace）として出したいとき（diff）。オブジェクトのキーは UTF-16 の符号単位の順に歩きます。",
          "patch の逆を出したいとき（invert）。逆にできない操作（old の無い remove・replace）は NOT_INVERTIBLE として全部並べ、そのときは逆の patch を出しません。",
          "patch を当てた候補を、確定せずに出したいとき（apply）。base_revision が document_revision と違えば STALE_BASE、保護された位置に触れれば PROTECTED_PATH として、当てる前に全部並べます。committed はいつも false です。",
          "目的（intent）ごとの取り消しの計画を出したいとき（plan_undo）。intent が同じ変更を新しい順に逆にしてつなぎ、後の取り消さない変更と重なる位置は LATER_CHANGE_OVERLAPS として並べます。",
          "AI がした intent の割り当て（intent_origin: ai_candidate）を事実にせず、candidate_intent_changes に分けたいとき。その変更は取り消しに入れず、残す変更として重なりの相手にします。",
          "時計・乱数・環境の locale を使わず、同じ入力から byte 単位で同じ出力が要るとき。外の境界・状態はありません。"
        ],
        "do_not_use_when": [
          "変更を確定・保存したいとき、版を採番したいとき。原子的な適用・保存・版の採番はこの部品の外の役目で、apply は候補を出すだけです。",
          "文字列の中の文字単位の差分が要るとき。文字列が違えば replace 1 つです（文書の文字の断片編集は P-09 の範囲）。",
          "配列の要素の挿入・移動を見つけたいとき。並べ合わせ（LCS など）はせず、先頭への挿入は後ろの要素の replace の列になります。",
          "RFC 6902 の JSON Patch として使いたいとき。move・copy・test と配列の末尾の \"-\" は扱わず、old を持つ点・add が既にあるキーに置かない点でも別のものです。",
          "自然文の目的を変更に対応づけさせたいとき。割り当ては呼び出し側が決め、AI がした割り当ては ai_candidate として渡します。",
          "外部への送信や決済の取り消しに使いたいとき。対象外です。",
          "意味の上での依存（ある変更が別の変更を前提にしていること）を見たいとき。位置の重なりでしか見ません（意味の依存は P-04 の範囲）。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が決まったキーだけを持つ素のオブジェクトでないとき（知らないキー・足りないキー・getter・列挙されないプロパティ・symbol のキー）INVALID_ARGUMENT、値を調べること自体が例外になるとき INSPECTION_FAILED、operation が 4 つのどれでもないとき UNSUPPORTED_OPERATION で止めます。",
          "文書・value・old が JSON の値でないとき（undefined・NaN・単独のサロゲート・__proto__ というキー・穴のある配列・配列の派生クラス・クラスのインスタンスなど）INVALID_VALUE で止めます。",
          "patch の op が move・copy・test のとき UNSUPPORTED_OP、patch の形が違うとき（add・remove の path が根）INVALID_PATCH、JSON Pointer の形が違うとき（__proto__ という token を含む）INVALID_POINTER で止めます。",
          "版が 1〜128 文字の整った文字列でないとき INVALID_REVISION、origin・intent_origin が語彙の外のとき INVALID_ORIGIN、変更の形が違うとき INVALID_CHANGE、同じ change_id が 2 つあるとき DUPLICATE_CHANGE_ID、上限（操作 10,000・深さ 64・値 200,000 個など）を超えたとき RESOURCE_LIMIT（limit つき）で止めます。",
          "apply では、STALE_BASE と PROTECTED_PATH（等しい・下・上・配列の詰め替えで動く位置）を当てる前に全部並べ、どちらかがあれば当てません。無ければ写しの上で順に当て、PRECONDITION_FAILED（old の不一致・add の位置に値がある）か PATH_NOT_FOUND（\"-\"・先頭の 0 を含む）の最初の 1 つで止めます。",
          "plan_undo では、履歴の途切れ（HISTORY_GAP）・現在の版との違い（STALE_BASE）・逆にできない変更（NOT_INVERTIBLE）・後の変更との重なり（LATER_CHANGE_OVERLAPS）を conflicts に並べ、1 つでもあれば undo_patch を出しません。取り消す変更が無ければ no_matching_change です。",
          "apply(base, diff(base, target)) の候補が target と等しく、apply(target, invert(diff(base, target))) の候補が base と等しいことを、単体試験とファズで確かめました。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "結果は差分・逆・候補・計画の事実の提示で、変更の確定・保存・取り消しの実行ではありません。どの変更をどの目的に属させるか、候補を採用するかは、この部品の外（利用者・Runtime）で決めます。",
          "配列の詰め替えの判定は token の形だけで行います。数字の形のキーを持つオブジェクトでも詰め替えとして扱い、多めに止めます。",
          "need_input は使いません。facts.state はいつも done で、止める理由は refusals・conflicts・not_invertible に並べます（このとき ok は true）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/patch-conflict.js の patchConflict({ operation, ... }) を呼びます。diff・invert・apply・planUndo を直接呼ぶこともできます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state: \"done\"・operation と操作ごとの事実、inference: null・advice: null）か { ok: false, errors: [{ code, path }] }（RESOURCE_LIMIT は limit も）です。履歴・版・保存は呼び出し側が持ちます。副作用・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-04",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "来歴・依存のグラフ — 来歴・依存グラフの部品（Kernel）",
        "summary": "来歴・依存グラフの部品（Kernel）は、版つきの node と辺（登録済み registered と AI の候補 ai_candidate）、変更の集合から、登録済みの辺だけで下流へ届く node（impacted、最短の経路つき）と、候補の辺を使わないと届かない node（candidate_impacted）を分けて出す単独の部品です。循環・無い node や版の違う辺・権限外の node・届かなかった node も事実として出します。inference と advice はいつも null です。",
        "problem": "変更の影響範囲は、表からは見えません。AI の候補の依存を登録済みの依存と混ぜて事実にすること、欠けた辺や版の違いを黙ってたどること、届かなかった node を「無関係」と扱うことを防ぎます。",
        "use_when": [
          "変更（changed）から、登録済みの辺だけで影響が届く node を、深さと最短の経路（path・edges）つきで出したいとき（analyzeProvenance）。同じ長さの経路は node の id の並びがいちばん小さいものを選びます。",
          "AI の候補の辺（ai_candidate）を使わないと届かない node を candidate_impacted に分け、経路の中の候補の辺の index も受け取りたいとき。impacted と candidate_impacted は重なりません。",
          "循環を、登録済みの辺だけの強連結成分（cycles）と、候補の辺で初めてできる・大きくなる成分（candidate_cycles）に分けて受け取りたいとき。",
          "無い node への辺（NODE_ABSENT）と版の違う辺（VERSION_MISMATCH）を missing として受け取り、それらをたどらずに state を need_input にしたいとき。",
          "権限外の node（allowed: false）について、届いても id だけを not_allowed に出し、kind や version を出さないようにしたいとき。到達はその node を通って続けます。",
          "探索の深さ（max_depth、1〜256、無ければ 256）を決め、深さで切ったことを depth_limit_hit（登録済みの探索と、候補を入れた探索の別）で知りたいとき。",
          "時計・乱数・環境の locale を使わず、node の並びやキーの並びに関係なく、同じ入力から byte 単位で同じ出力が要るとき。"
        ],
        "do_not_use_when": [
          "影響の有無や修正の要否を判断させたいとき。到達と欠けの事実を出すだけです。",
          "届かなかった node が無関係だと示したいとき。unreached は探索の中で届かなかったという事実で、無関係であることの証明ではありません（unreached_meaning: not_reached）。",
          "辺の種類（kind）ごとにたどり方を変えたいとき。kind は読まず、どの kind も同じにたどります。",
          "版の前後関係を見たいとき。from_version・to_version は文字列の一致だけを見ます。",
          "allowed の印が正しいか（権限の真正性）を確かめさせたいとき、権限外の node の中身を遮らせたいとき。どちらも Host の役目で、Host は権限外の node の中身を渡しません。",
          "辺の向きを部品に決めさせたいとき。辺 { from, to } は「from が変わると to に響く」の向きで渡します（来歴なら 元 → 派生）。",
          "Node.js v24.21.0 のほかの版で使うとき（NOT_VERIFIED）や、上限いっぱいの入力の時間とメモリーを前提にしたいとき。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーは測っていません。"
        ],
        "what_it_checks": [
          "入力が素のデータでないとき（getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ）、知らないキー・足りないキー・null・語彙の外の値（origin が registered・ai_candidate 以外など）・1〜128 文字でない id・単独のサロゲートがあるとき INVALID_INPUT で止めます。",
          "max_depth が 1〜256 の整数でないとき INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy）INSPECTION_FAILED、id が重なるとき DUPLICATE_ID で止めます。",
          "上限（nodes 100,000 件・edges 400,000 件・changed 100,000 件・出力の経路の node id の合計 4,000,000）を超えたら、どの上限かを limit に入れて RESOURCE_LIMIT で止めます。失敗を空の成功にしません。",
          "辺の from_version・to_version が書かれていて、その node の version と違えば VERSION_MISMATCH、端の node が無ければ NODE_ABSENT として missing に出します。その辺はたどらず、循環の計算にも入れません。",
          "版の違う変更の node は、変わったことを Host が述べた事実として始点にし、VERSION_MISMATCH も出します。変更された node 自身は impacted・candidate_impacted・unreached のどれにも入れません。",
          "止めるときは最初に見つけた 1 つだけを { code, path, limit? } で返します。path は JSON Pointer（入力全体は空文字）です。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。AI 由来（ai_candidate）のものを事実に変えません。",
          "結果は到達と欠けの事実の提示で、影響の有無・修正の要否の判断ではありません。影響の判断や修正の計画の代行でもありません。最終の権限・遮断・適用は Host と Runtime の役目です。",
          "max_depth で切った場合、登録済みの辺では深い位置にある node が、候補の辺の短い経路で candidate_impacted に入ることがあります（そのとき depth_limit_hit.registered は true です）。",
          "入出力の形は v1 で定めたもので、使う側の受け入れはまだ済んでいません。",
          "ファイル・通信を使わず、資格情報を持ちません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/provenance-graph.js の analyzeProvenance({ nodes, edges, changed, max_depth? }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state・impacted・candidate_impacted・cycles・candidate_cycles・missing・not_allowed・unreached・depth_limit_hit・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用・状態・外の接続・資格情報は無く、依存パッケージも無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-05",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "制約・割当・時間枠 — 制約・割当・時間枠の部品（Kernel）",
        "summary": "制約・割当・時間枠の部品（Kernel）は、整数の分で書いた工程（所要時間・不可分か・分けるときの最小・前提・資源・時間窓）と枠（始まり・終わり・資源）から、決まった順の上限つき探索で、割当の案・成り立たない理由・探索の上限に届いたことを事実として返す単独の部品です。infeasible は証明できたときだけで、上限で止まったときは search_limit と分けます。不明は推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "予定を組むときに、不可分の工程を短い枠へ縮めて入れてしまうこと、探索を途中で打ち切っただけなのに「成り立たない」と言ってしまうこと、最適だと証明できていない案を最適として出すこと、分からない所要時間や時間窓を推測で埋めることを防ぎます。",
        "use_when": [
          "工程（0〜200 件）と枠（0〜200 件）から、目的 objective に合わせて割当の案を出したいとき（solveSchedule）。目的は feasible（全部置ける案）・min_unassigned（外す工程の数の最小）・earliest_finish（全部置いたときのいちばん遅い終わりの最小）の 3 つです。",
          "不可分の工程（indivisible: true）を、1 つの枠に duration.min の長さのまま丸ごと置きたいとき。枠と時間窓の重なりが min より短ければ、その枠には入れません（20 分の不可分工程を 10 分の枠へ入れません）。",
          "分けられる工程を、どの部分も splittable_min 以上、合計がちょうど duration.min になるように分けて置きたいとき。同じ工程の部分は時間が重ならず、part は時間の順に 1 から振られます。",
          "前提（requires）・資源（resource）・時間窓（window）を守った割当が要るとき。前提の工程はこの工程のどの部分よりも前に終わり、前提の工程が置かれなければこの工程も置きません。資源は工程と枠の両方にあるときだけ一致を求めます。",
          "成り立たないことを、理由のコードつきで受け取りたいとき。静的な理由（NO_SLOTS・NO_SLOT_FOR_RESOURCE・WINDOW_EMPTY・INDIVISIBLE_TOO_LONG・CAPACITY_TOO_SMALL・PRECEDENCE_CYCLE）と、探索を尽くした印 SEARCH_EXHAUSTED を reasons に出します。",
          "探索の手数を max_nodes（1〜1,000,000）で区切り、上限に届いたことを search_limit_reached と status（search_limit）で、最適性を optimality（proven か not_proven）で区別して受け取りたいとき。",
          "AI 由来の工程（origin: ai_candidate）を含むかどうかを、割当の行の origin・ai_candidate_tasks・plan_includes_ai_candidate で分けて受け取りたいとき。",
          "時計・乱数・環境の locale を使わず、配列の並びやキーの並びに関係なく、同じ入力から byte 単位で同じ出力が要るとき。"
        ],
        "do_not_use_when": [
          "案を採るか、工程を分け直すか、所要時間が正しいかを判断させたいとき。結果は割当の案と成り立たない理由・探索の上限の事実で、採用・実行の判断ではありません。",
          "工程の分け方や所要時間を部品に決めさせたいとき。どちらも呼び出し側が決めて渡します。この部品は工程を作らず、所要時間を推測しません。",
          "同じ人の時間の重なる 2 つの枠に、違う工程を同時に置くことを止めさせたいとき。枠どうしは独立の受け皿として扱います。1 人の予定なら、呼び出し側が重ならない枠を渡します。",
          "工程の間の移動・準備の時間、暦・時間帯、分より細かい時刻を扱いたいとき。時刻と長さは 0〜100,000,000 の整数の分だけです。",
          "重み・優先・柔らかい制約・締切の遅れの最小などの目的が要るとき。目的は 3 つだけで、ほかは UNSUPPORTED_OBJECTIVE で止めます。min_unassigned は工程の数を数え、長さは数えません。",
          "min より長くかかる工程の余裕を取りたいとき。duration.max は形を検めるだけで、置く長さはいつも duration.min です。",
          "大きな問題を決まった時間で解きたいとき。探索は指数的で、大きな問題では search_limit になりえます。速さは約束しません。max_nodes に達すると必ず止まります。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が { tasks, slots, objective, search } の 4 つのキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED、素のデータでないとき（getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・symbol のキー・__proto__ のキー・列挙されないキー）NOT_PLAIN_DATA で止めます。素のデータに写してから検め、検めた値と探索に使う値は同じです。",
          "工程の形が違うとき（知らないキー・足りないキー・id・origin・indivisible の形、duration.min が 1 未満・max が min 未満・整数でない、不明の印の形の違い、不可分の工程の splittable_min・min を超える splittable_min、requires の重複・配列でない、時間窓の end が start 以下・負の値）INVALID_TASK で止めます。null は受け取りません。",
          "枠の形が違うとき（end が start 以下・整数でない・負など）INVALID_SLOT、search が { max_nodes } でないとき INVALID_SEARCH、objective が 3 つの目的のどれでもないとき UNSUPPORTED_OBJECTIVE、同じ id の工程か枠が 2 つあるとき DUPLICATE_ID、requires が無い工程の id を指すとき UNKNOWN_TASK_REF で止めます。",
          "上限（工程 200 件・枠 200 件・前提は工程ごとに 200 件・max_nodes 1,000,000・時刻と長さ 100,000,000 分・配列 4096 要素・入れ子 4 段）を超えたら、どの上限かを limit に入れて RESOURCE_LIMIT で止めます。止めるときは { code, path, limit? } を返し、path は JSON Pointer です。",
          "duration か window が不明の印 { unknown: true } のとき、推測せず state を need_input にし、足りないものを need_input に並べます。探索はせず、status・assignment・objective_value は null、reasons は空です（途中までの結果を出しません）。",
          "feasible と earliest_finish では、静的な理由が 1 つでもあれば探索せず infeasible にします（nodes_explored は 0）。上限に届いて案が無ければ infeasible と言わず search_limit にし、上限に届いた後に残った案は not_proven にします（search_limit_reached が true なら nodes_explored = max_nodes）。",
          "結果を、src の補助関数を使わずに規則から書き直した独立オラクル（小さな問題の総当たりと割当の検算を含む）と突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは割当の事実だけです。inference と advice はいつも null で、出力のどの文字列にも直し方や優先の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。AI 由来の工程（ai_candidate）を事実に変えず、正しいかも判断しません。",
          "結果は割当の案と成り立たない理由・探索の上限の事実の提示で、予定の判断や実行ではありません。案を採るか、工程を分け直すか、所要時間が正しいかは、この部品の外で利用者が決めます。",
          "探索が完全であること（全部置ける案があれば見つかること）は、左詰めの議論と、小さな問題の総当たりとの 1,000,000 回の突き合わせで確かめました。形式的な証明はしていません。",
          "総当たりの突き合わせは、工程 4 件・枠 3 件・時刻 20 前後までの小さな問題だけです。それより大きな問題の infeasible・proven は、総当たりでは確かめていません。",
          "nodes_explored はこの部品の決まった数え方で、ほかの解き方の手数とは比べられません。同じ値の案のどれを出すかは探索の順で決まります。",
          "入出力の形と手数の数え方は v1 で定めたもので、既存の実装はありません（新規）。",
          "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/constraint-schedule.js の solveSchedule({ tasks, slots, objective, search }) を呼びます（例外を投げません。入力を書き換えません）。返すのは { ok: true, errors: [], facts }（state・status・optimality・assignment・unassigned・reasons・need_input・search_limit_reached など、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用・状態・外の接続・資格情報は無く、依存パッケージも無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-06",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "モデル・限定ルールの評価 — モデル・限定ルール評価の部品（Kernel）",
        "summary": "モデル・限定ルール評価の部品（Kernel）は、呼び出し側が渡すモデル（JSON の木の式）と限定ルール（条件と効果だけの小さな DSL）を、決まった上限の中で評価する単独の部品です。evaluateModel は 10 進の BigInt 計算（精度 34 桁、half_even）で出力ごとの値と単位を出し、evaluateRules・runRules は役ごとの合法手・次の状態（候補）・終わりと結果を出します。繰り返し・再帰・コードの実行は受け取りません。UNKNOWN は推測せず need_input にし、形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "試算や判定の式を評価するときに、浮動小数点の誤差が値に入ること、0 での割り算や範囲外の結果が 0 や NaN として紛れ込むこと、欠けた入力を推測で埋めて途中までの計算を出してしまうこと、規則の評価が止まらない繰り返しやコードの実行になることを防ぎます。乱数も明示の seed からだけ作るので、同じ入力と seed から同じ結果を出します。",
        "use_when": [
          "モデル（params で引数と単位を宣言し、outputs に出力ごとの式を書いたもの）と inputs（値・単位・origin）から、出力ごとの値と正準の単位を 10 進の文字列で出したいとき。式の演算は + - * / min max neg、round（scale 0〜34、half_even・half_up・floor・ceil）、rand です。",
          "1 つの引数を sweep で 1〜1000 点動かし、点ごとの値を並べたいとき（点の数 × 節の数は 1,000,000 まで）。",
          "乱数を含む式を、明示の seed（0〜2^53−1 の整数）から再現できる形で評価したいとき。生成器は seed から SplitMix64 で 4 語を作る xorshift128 で、出力ごと・走査の点ごとに始め直します。",
          "単位の次元をそろえて計算したいとき。単位は記号ごとの指数の組で比べ、* と / は指数を足し引きし、+ - min max は次元がそろうことを求めます。",
          "役（roles）・手（moves の when と effects）・終わり（end）で書いた限定ルールと状態から、evaluateRules で役ごとの合法手、手を渡したときの次の状態（候補）か ILLEGAL_MOVE と成り立たなかった最初の条件、終わりと結果を出したいとき。",
          "手の列を runRules で max_steps（1〜1000）まで順に当て、当てられなかった最初の手で止めて、その前までの状態を final_state として受け取りたいとき。",
          "AI 由来（origin が ai_candidate）のモデル・入力・走査・規則を使ったかどうかを、basis（registered_only か includes_ai_candidate）と candidate_sources の位置で分けて受け取りたいとき。"
        ],
        "do_not_use_when": [
          "結果を実際の需要の予測として使いたいとき。結果は渡された式の計算です。モデルの式・規則・入力の値が正しいかは確かめません。",
          "単位の換算（例えば m と km）を部品にさせたいとき。同じ次元の別の単位は別の記号として扱います。係数を掛けるなら式に書きます。",
          "暗号用の乱数や、統計の検定を通った乱数が要るとき。xorshift128 は暗号用ではなく、統計の検定はしていません。",
          "引数のある手、同時の手番、隠れた情報の確率、時間切れを含む規則を扱うとき。限定ルールにはこれらがありません。",
          "繰り返し・再帰・式の評価・コードの実行を規則に書きたいとき。語彙の外は UNSUPPORTED_CONSTRUCT で止めます。",
          "状態に真偽値・null・小数を入れたいとき。状態の値は整数・文字列・配列・オブジェクト・UNKNOWN の印 { unknown: true } だけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・null・__proto__ のキーは受け取らず、素のデータに写してから検めます。",
          "入力の形が違うとき（知らないキー・足りないキー・語彙の外の origin・名前の形・重複・JSON Pointer の形など）INVALID_INPUT、10 進の文字列が決まった形でない・有効桁が 34 桁を超える・範囲（0 か 10^-100 ≤ |値| < 10^100）の外・\"-0\" のとき INVALID_DECIMAL、単位の文字列の形が違う・合わせた指数が 64 を超えるとき INVALID_UNIT で止めます。",
          "宣言の無い引数・役を指すとき UNKNOWN_NAME、足し算・引き算・min・max の単位の次元が違うときと入力・走査の単位が宣言と違う次元のとき UNIT_MISMATCH、式に rand があるのに seed が無いとき SEED_REQUIRED、式の演算子・丸めの方法・条件や効果の op が語彙の外のとき UNSUPPORTED_CONSTRUCT で止めます。",
          "上限を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。上限は、式の深さ 64・節 10,000・走査 1000 点・状態 256 KiB と深さ 64・文字列 4096 文字・効果の値 4 KiB・max_steps 1000 などです。手を当てた後の状態の大きさもここで確かめます。",
          "使う引数に欠け（MISSING）か UNKNOWN が 1 つでもあれば、推測せず state を need_input にし、points を空にします（途中までの計算を出しません）。0 での割り算と範囲外の結果は、その出力だけ DIVISION_BY_ZERO・OUT_OF_RANGE と起きた節の位置で示し、値を 0 や NaN にしません。",
          "手は 定義→役→終わり→条件→効果 の順に確かめ、合法でなければ ILLEGAL_MOVE と成り立たなかった最初の条件（または MOVE_NOT_DEFINED・ROLE_MISMATCH・ENDED）を返します。効果を当てられなければ EFFECT_FAILED とし、手全体を捨てます（next_state は null）。状態の UNKNOWN の奥を読む条件や効果は、合法とも違反とも言わず undetermined にし、印の位置を need_input に並べます。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは計算の事実だけです。inference と advice はいつも null で、出力のどの文字列にも判断の語を入れません。結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "結果は渡された式と規則の評価の事実で、モデルや規則が正しいこと、実際の需要の予測、遊びとしての良し悪しの判断ではありません。需要の予測・遊びの設計・規則の判定の代行もしません。式・規則・入力の値が妥当かは、使う側で確かめます。",
          "コードに固有の式や遊びの規則を持ちません。式・単位・規則・状態はすべて入力から受け取ります。",
          "34 桁の丸めを節ごとに重ねたときの誤差の大きさは評価しません。数値の不安定さとして出すのは precision_rounded と OUT_OF_RANGE の印だけです。",
          "状態の上限（256 KiB・深さ 64）は 1 手を当て終えた後に確かめ、1 手の途中の大きさは見ません。",
          "時計・環境の locale・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/model-rule.js の evaluateModel({ model, inputs, sweep?, seed? })、evaluateRules({ rules, state, role, move?, max_steps? })、runRules({ rules, state, moves, max_steps }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（state が done か need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-07",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "根拠・主張・適用条件 — 根拠・主張・適用条件の部品（Kernel）",
        "summary": "根拠・主張・適用条件の部品（Kernel）は、主張（fact・testimony・inference・hypothesis の型と、出典の ref・version・locator つき）ごとに、成立条件が現在の条件に当てはまるかを match・mismatch・unknown の 3 つで返す単独の部品です。部分的な一致は match にしません。出典の版が現在の版と違えば stale とし、評価を unknown（SOURCE_CHANGED）にします。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "「その根拠は今も使えるか」を確かめるときに、一部の条件だけが合う主張を全面的に当てはまるとしてしまうこと、資料が更新されたのに前の評価を使い続けること、「不明と入力された」と「まだ入力されていない」を混ぜてしまうこと、推論や仮説を事実に格上げしてしまうことを防ぎます。",
        "use_when": [
          "主張（0〜10,000 件）ごとの条件（0〜50 件、op は eq・ne・in・lt・le・gt・ge）を、current.conditions の現在の値と比べ、applies を match・mismatch・unknown で出したいとき。偽の条件があれば mismatch、偽が無く不明・未入力・比べられないものがあれば unknown、すべて真なら match です。",
          "出典の版（source.version）を current.source_versions と比べ、source_status（current・changed・not_entered・unknown）と stale を主張ごとに出したいとき。出典が current でなければ applies は unknown です。",
          "{ \"unknown\": true } と入力された値と、キーが無い（未入力の）値を分けたいとき。未入力だけを need_input（CONDITION_NOT_ENTERED・SOURCE_VERSION_NOT_ENTERED・SUPPORT_NOT_ENTERED）に並べ、state を need_input にします。0・false・空の文字列は値として扱います。",
          "主張の型（type）と出どころ（origin）をそのまま保ち、AI 由来（ai_candidate）の主張を candidate_claims に分けて受け取りたいとき。",
          "supports で書いた主張どうしの支えの関係を、主張ごとに型つきの最短の鎖（id・type・origin・depth・chain）として並べ、循環を support_cycles と in_support_cycle で知りたいとき。循環は止めずに出します。",
          "applies が unknown や mismatch になった理由を、reasons（決まった順）と failed・unknown_keys・missing_keys・not_comparable_keys で主張ごとに受け取りたいとき。"
        ],
        "do_not_use_when": [
          "主張が正しいか、採用するかを判定させたいとき。match は渡された条件と現在の値の照合の結果で、主張が真実であることは示しません。主張の内容と出典の位置（locator）の正しさも確かめず、locator はそのまま返すだけです。",
          "小数・日付・範囲・文字列の大小・正規表現で条件を比べたいとき。比べるのは文字列・整数・真偽値の一致と整数の大小だけです（日付は呼び出し側が整数にして渡します）。",
          "出典の版の前後関係（どちらが新しいか）を見たいとき。新旧は版の文字列の一致だけで見ます。出典の現在の版は呼び出し側が確かめて渡し、この部品は資料を読みません。",
          "支えるものの結果で、支えられる主張の applies を変えたいとき。applies は自分の条件だけで決まります。",
          "支えの鎖をすべて列挙したいとき。出すのは最短の鎖 1 本だけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "入力の形が違うとき（素のデータでない・知らないキー・足りないキー・語彙の外の値・型が違う・null・id が 1〜128 文字でない・単独のサロゲート・集合に同じ値が 2 回）INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy など）INSPECTION_FAILED で止めます。",
          "getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ・null は受け取りません。読んだ値の写しだけで計算し、入力を書き換えません。",
          "id が重なるとき DUPLICATE_ID、条件の演算子が eq・ne・in・lt・le・gt・ge のどれでもないとき UNSUPPORTED_OPERATOR で止めます。in の値が 0 件か 65 件以上のとき、lt・le・gt・ge の値が安全な整数でないときも止めます。",
          "上限（claims 10,000 件・1 つの主張の conditions 50 件・in の値 64 件・supports 64 件・current.conditions と source_versions 各 10,000 個・出力の支えの鎖の id の合計 2,000,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
          "等しさは型も含めて厳密に見ます（\"5\" と 5、true と 1 は違う）。lt・le・gt・ge で現在の値が整数でないときは偽にせず not_comparable とし、applies を unknown にします。",
          "止めるときは、最初に見つけた 1 つだけを { code, path, limit? } で返します（path は JSON Pointer）。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
          "結果は条件の当てはまりと出典の版の事実の提示で、主張が正しいかの判断でも、採否の判断の代行でもありません。主張の真実性は約束せず、権利の認定もしません。最終の権限・遮断・適用は Host と Runtime の役目です。",
          "支えの鎖に fact があっても、inference・hypothesis を格上げしません。主張をまとめることもしません。",
          "条件のキーと値の意味（何をもって成立とするか）は決めません。呼び出し側が決めて渡します。",
          "入出力の形はこの部品の v1 で定めたもので、使う側の受け入れはまだです。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーの測定はしていません。",
          "時計・乱数・環境の locale・外の境界・状態・資格情報を持たず、ファイル・通信を使いません。副作用は NONE です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/evidence-claim.js の evaluateClaims({ claims, current }) を呼びます（例外を投げず、入力を書き換えません）。受け取るのは主張の一覧 claims と、現在の条件と出典の版 current です。返すのは { ok: true, errors: [], facts }（claims・candidate_claims・support_cycles・need_input、inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-08",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "表示モデル・操作の変換 — 表示モデル・操作変換の部品（Kernel）",
        "summary": "表示モデル・操作変換の部品（Kernel）は、登録された表示契約（view_id・version・entity_kind・fields・commands。宣言だけ）に従い、共通の entity（id・version・kind・data）から欄ごとの表示モデルを作り（project）、型付きの操作 event を型付きの command の候補に変える（command）単独の部品です。データに無い欄・不明・型の違う値を空にせず印で出し、違反と不足は事実として返します。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "画面と処理のあいだの変換で、データに無い欄を空の値と取り違えること、表示の設定で編集できない欄を編集できるようにしてしまうこと、表示の定義にスクリプトや関数が混ざること、操作のたびに AI を呼ぶことを防ぎます。",
        "use_when": [
          "表示契約の fields（path・label_key・kind（text・number・enum・list・bool）・editable・range・enum・max_length）に従って、entity.data から欄ごとの view（value・value_state・editable）を作りたいとき。",
          "欄の値を value_state（present・missing・unknown・kind_mismatch・out_of_contract）で分けたいとき。データに無い欄は { missing: true }、{ unknown: true } は不明のまま、型の違う値は { kind_mismatch: true } として出し、空の文字列・0・false・空の一覧と区別できます。",
          "settings（hidden・order・editable）で欄を隠す・並べ替える・編集不可にしたいとき。隠した欄は view から除き、hidden に並べます。",
          "操作 event（event_type・event_id・path・value）を、(event_type, path) の組で選んだ command 定義（op は set・increment・select・toggle）に照らし、型付きの command（op・path・value・base_version・event_id・event_type）の候補に変えたいとき。base_version には渡した entity_version を写します。",
          "操作の違反（UNKNOWN_EVENT・NOT_EDITABLE・VALUE_KIND_MISMATCH・RANGE_VIOLATION）と、値が無い・不明の event の need_input（VALUE_ABSENT・VALUE_UNKNOWN）を、ok: false ではなく事実として受け取りたいとき。"
        ],
        "do_not_use_when": [
          "画面を描画したいとき、権限を判断したいとき、操作を適用したいとき。この部品は表示と操作の変換だけをします。描画は App、権限・基底版の照合・適用は Host（Runtime）の役目です。",
          "表示契約が登録済みか、版が正しいか、entity の版が今の版かを確かめたいとき。渡された契約と版を写して返すだけです。",
          "小数や 10 進の文字列の数を扱うとき、文字列以外の要素の一覧を扱うとき。number は安全な整数だけ、list は文字列の配列だけです。",
          "表示の文言の辞書や翻訳が要るとき。label_key は id の形だけを検め、文言は持ちません（App の役目）。",
          "同じ event_id の再送を見分けたいとき。重複は判定しません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null は受け取らず、素のデータに写してから検めます。",
          "どこかに関数があるとき、表示契約・設定に宣言に無いキーがあるとき SCRIPT_REFUSED で止めます。式やスクリプトを評価する経路はありません。",
          "表示契約の形が違うとき（kind・op の語彙の外、op と kind の組の違い、min > max、enum の空や重なり、max_length の範囲外、path や (event_type, path) の重なり、command の path が欄に無いなど）INVALID_CONTRACT で止めます。command の制約 allowed で欄の enum を広げることもここで止めます。",
          "entity の形が違うとき INVALID_ENTITY、entity.kind が契約の entity_kind と違うとき ENTITY_KIND_MISMATCH、設定の形が違うとき INVALID_SETTINGS、設定が契約に無い path を名指しするとき SETTINGS_PATH_UNKNOWN、event の形が違うとき INVALID_EVENT で止めます。",
          "契約が編集不可の欄を設定で編集可にしようとしたとき EDITABLE_WIDEN_REFUSED で止めます。表示の違いで権限を増やしません。command は契約の editable だけを見ます。",
          "command では、組が無ければ UNKNOWN_EVENT、欄が編集不可なら NOT_EDITABLE、値の型が合わない・toggle に値があるとき VALUE_KIND_MISMATCH、範囲を外れれば RANGE_VIOLATION を返します。値は欄の制約と command の制約の両方を満たすときだけ通します。",
          "上限（入れ子 32 段・値の数 200,000・文字列 65,536・配列 10,000 要素・欄 256・command 256・enum と allowed 各 256）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは変換の事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。AI を呼ばず、時計・乱数・ファイル・通信も使いません。",
          "command は候補で、適用ではありません。適用・基底版の照合・権限の真正性・同時更新の競合は Host（Runtime）の役目です。",
          "increment の適用後の値が欄の range に入るかは確かめません。基底版の値を持たないため、command の range で増減の幅だけを限ります。",
          "out_of_contract の値は隠さずそのまま写して出します。kind_mismatch の値は出しません。",
          "画面・描画エンジン・権限の仕組みではありません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/view-model.js の project({ contract, entity, settings? }) と command({ contract, entity_version, event }) を呼びます（どちらも例外を投げず、入力を書き換えません）。返すのは { ok: true, errors: [], facts }（project は view・hidden、command は command・violation・need_input。inference: null・advice: null）か { ok: false, errors: [{ code, path, limit? }] }（path は JSON Pointer）です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port はありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-09",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "文書のアンカー・断片の編集 — 文書アンカー・断片編集の部品（Kernel）",
        "summary": "文書アンカー・断片編集の部品（Kernel）は、ブロックに分けた文書の中の位置（アンカー）を改版後の文書で探し直し（exact・relocated・ambiguous・lost）、断片の編集の差分と新しいブロックの文を候補として出す単独の部品です。位置は UTF-16 の符号単位で数え、サロゲートの組の間を位置にしません。候補が 2 か所以上なら全部並べて選びません。保護範囲に重なる編集は PROTECTED_RANGE として差分を出しません。inference と advice はいつも null です。",
        "problem": "コメントや参照の位置が、文書の改版でずれる事故を防ぎます。改版後に曖昧になった位置を勝手に 1 つに決めないこと、絵文字などのサロゲートの組を割った位置を作らないこと、保護された文字に触れる編集を差分にしないこと、消した文字を失わないことを、決まった規則で行います。",
        "use_when": [
          "ある版で作ったアンカー（doc_version・block_id・start・end・quote・prefix・suffix）を、今の文書（version・blocks）の中で探し直し、status（exact・relocated・ambiguous・lost）と location を受け取りたいとき（operation: resolve）。",
          "探し直しを決まった段の順で行いたいとき。exact（同じ版・位置の文字が quote と同じ）→ 前後つき（prefix+quote+suffix）が同じブロック → 前後つきが全部のブロック → quote だけが同じブロック → quote だけが全部のブロック の順で、一致が 1 つでもあった最初の段で決めます。",
          "曖昧な位置を確定させたくないとき。一致が 2 つ以上なら ambiguous とし、location を null にして候補を全部並べます（一覧は 1,000 件まで、candidate_count は全部の数、candidates_truncated で切ったことを示します）。",
          "ブロックの [start, end) を replacement に置き換える断片の編集から、diff（消した文字 removed と入れた文字 inserted を含む）と new_block_text を候補として受け取りたいとき（operation: edit）。replacement の出どころ（registered・human・ai_candidate）は replacement_origin として返します。",
          "呼び出し側が決めた保護範囲（protected）に重なる編集を止めたいとき。重なれば status を refused とし、violations に PROTECTED_RANGE を並べ、diff と new_block_text を null にします。"
        ],
        "do_not_use_when": [
          "置き換えた後に前後の文の意味が保たれるかを判定させたいとき。見るのは文字の保護だけで、meaning_checked はいつも false です。",
          "Unicode の正規化（NFC・NFKC）、大文字・小文字・空白の違いを同じとみなしたいとき、似た文をあいまいに探し直したいとき。比べるのは符号単位だけです。",
          "書記素（結合文字・絵文字の ZWJ 列・異体字セレクタ）の境目を守りたいとき。割らないのはサロゲートの組だけです。",
          "ブロックをまたぐアンカーや編集を扱うとき、ブロックの分割・結合を追いかけたいとき。",
          "文書の保存、版の採番、共同編集の競合の処理、出典（どの資料のどの版か）の保持が要るとき。この部品はアンカーと版を返すだけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が決まったキーだけを持つ素のオブジェクトでないとき（知らないキー・足りないキー・getter・列挙されないプロパティ・symbol のキー）INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED、operation が resolve・edit のどちらでもないとき UNSUPPORTED_OPERATION で止めます。",
          "文書の形が違うとき（単独のサロゲートを含む文など）INVALID_DOCUMENT、同じ id のブロックが 2 つあるとき DUPLICATE_BLOCK_ID、アンカーの形が違うとき（start > end、quote の長さが end − start でない、prefix・suffix が 32 符号単位を超えるなど）INVALID_ANCHOR で止めます。",
          "編集の形が違うとき INVALID_EDIT、origin が語彙の外のとき INVALID_ORIGIN、保護範囲の形が違う・空・文書に無いブロック・長さを超える・組の間のとき INVALID_PROTECTED、編集する block_id が無いとき BLOCK_NOT_FOUND、end が文の長さを超えるとき INVALID_RANGE、start か end が組の間のとき SURROGATE_SPLIT で止めます。",
          "上限を超えたとき RESOURCE_LIMIT で止め、limit に blocks（10,000）・document_bytes（文の UTF-8 の合計 1,048,576 バイト。編集の後の文書も）・protected（1,000）のどれかを入れます。",
          "消す範囲 [start, end) が保護範囲と 1 文字でも重なるとき、または挿入（start = end）が保護範囲の内側（両端を除く）にあるとき、PROTECTED_RANGE を violations に並べ、差分を出しません（ok は true）。",
          "一致は重なりも数え（\"aaa\" の中の \"aa\" は 2 か所）、ブロックをまたいだ一致はしません。出力の位置にも、編集・保護の位置にも、サロゲートの組の間を使いません。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null で、resolve は文書の文を出しません。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
          "edit は候補を出すだけで、文書を確定・保存しません（committed はいつも false）。ambiguous の候補からどれを選ぶか、編集を採用するかは、この部品の外（利用者・Runtime）で決めます。",
          "結果は位置の候補と編集の候補の事実の提示で、位置の確定でも、意味が保たれることの約束でもありません。位置の確定や文書の編集の代行もしません。",
          "保護範囲は作りません。どこを保護するかは呼び出し側が決めて渡します。",
          "W3C Web Annotation の TextQuoteSelector・TextPositionSelector に近い形ですが、それに準拠するとは言いません（位置は UTF-16 の符号単位で、ブロックの id を持ちます）。",
          "時計・乱数・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/document-anchor.js の documentAnchor({ operation: \"resolve\" | \"edit\", ... }) を呼びます（resolve・edit を直接も呼べます。例外を投げません）。resolve は { document, anchor }、edit は { document, block_id, start, end, replacement, origin, protected } を受け取ります。返すのは { ok: true, errors: [], facts }（state は done、inference・advice は null）か { ok: false, errors: [{ code, path }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-10",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "イベントの集計・状態の還元 — イベント集計・状態還元の部品（Kernel）",
        "summary": "イベント集計・状態還元の部品（Kernel）は、宣言だけの reducer（event の種類ごとに set・add・append・remove の規則）で、版つきの state に events を配列の順に当て、新しい状態の候補（new_state）と、当てた event・重複・順序違反・区切り（window）の外・当てられなかったもの・種類ごとの件数を事実として出す単独の部品です。同じ event_id の再送は当てません。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "履歴から今の状態を作るときに、再送された event で数が二重に増えること、seq の飛びや逆行を見逃して状態を作ってしまうこと、区切りの途中で規則が替わること、1 つの event の規則が途中まで当たった中途半端な状態が残ること、無い値を 0 として足してしまうことを防ぎます。",
        "use_when": [
          "reducer（version と、種類ごとに 0〜64 件の規則 { op, path, from（\"payload.<キー>\"）か value }）で、state に events（0〜10,000 件の { event_id, seq, at, type, payload }）を順に当て、new_state の候補と aggregates.by_type（当てた event の種類ごとの件数）を受け取りたいとき。",
          "同じ event_id の再送を当てたくないとき。seen_event_ids にあれば duplicates（source: seen）、同じ束で 2 度目なら duplicates（source: batch）に並べ、増加は 0 です。",
          "seq の連続を確かめたいとき。直前の seq + 1 だけを受け取り、飛び（gap）は need_input（SEQ_GAP）、逆行（reversal）は order_violations に並べ、どちらもその後の event を not_applied にします。last_seq で最初に期待する seq を決められます。",
          "window（start・end・reducer_version）で区切りを決め、start ≤ at < end の外の event を outside_window にしたいとき（当てませんが seq は進めます）。",
          "1 つの event の規則を全部当てるか全く当てないかで扱いたいとき。当てられなければ、その event で変えたものをすべて戻し、apply_failures に event_id・rule_index・code を並べ、その後の event を not_applied にします。"
        ],
        "do_not_use_when": [
          "イベントの台帳・配信基盤・データベースとして使いたいとき。保存・確定・配信、重複の台帳・時刻・lease・永続化は Host（Runtime）の役目です。",
          "同じ event_id で中身の違う event を見分けたいとき。重複は event_id だけで判定し、中身は比べません。",
          "events を seq で並べ替えてから当てたいとき。配列の順に当てます。並べ替えが要るときは、渡す前に呼び出し側で行います。",
          "10 進の小数を足したいとき。add は安全な整数どうしだけです。",
          "配列の添字への set や、一覧の中の差し替えが要るとき。set は親がオブジェクトのときだけです。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が決まったキーだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null・関数は受け取らず、素のデータに写してから当てます。",
          "reducer の形が違うとき（知らないキー・op の語彙の外・path の形・__proto__ の token・from と value の数・from の形・add の value が整数でないなど）INVALID_REDUCER で止めます。reducer は宣言だけで、式・スクリプト・関数を評価する経路はありません。",
          "state の形が違うとき INVALID_STATE、events の形が違うとき（seq と at が 0 以上の整数でないなど）INVALID_EVENTS、seen_event_ids が id の一覧でないとき INVALID_SEEN、window の形が違うとき（start > end など）INVALID_WINDOW で止めます。",
          "window の reducer_version が reducer.version と違うとき RULE_SWITCH_IN_WINDOW で止めます。区切りの途中で規則を切り替えません。",
          "規則を当てられない理由を code で返します。種類の規則が無い NO_RULE_FOR_TYPE、payload にキーが無い PAYLOAD_KEY_MISSING、親がオブジェクトでない PARENT_NOT_OBJECT、対象が無い・整数でない・一覧でない TARGET_MISSING・TARGET_NOT_INTEGER・TARGET_NOT_LIST、値が整数でない VALUE_NOT_INTEGER、和があふれる INTEGER_OVERFLOW です。無い値・不明・文字列を 0 として扱いません。",
          "上限（入れ子 32 段・値の数 400,000・文字列 65,536・配列 100,000 要素・events 10,000・種類 256・種類ごとの規則 64・append 後の一覧 10,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
          "new_state は候補で、確定ではありません。保存・配信・適用の確定は Host の役目です。",
          "seen_event_ids が本当に当てた event か、state の版が今の版か、at が正しい時刻かは確かめません。どれも Host が渡します。時計は読まず、at と window は入力の値だけを使います。",
          "同じ seq の別の event（event_id が違う）は逆行として止めます。どちらが正しいかは決めません。",
          "値つきの remove の等しさは、キーの集まりと値の一致で見ます（数の -0 と 0 は等しい）。",
          "時計・乱数・ファイル・通信・外の境界・状態を持ちません。副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/event-reduce.js の reduceEvents({ state, state_version, reducer, events, seen_event_ids, window?, last_seq? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（new_state・applied・duplicates・order_violations・apply_failures・not_applied・aggregates・need_input など。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-11",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "構成・互換・試験計画の検査 — 構成 DAG・互換・試験計画検査の部品（Kernel）",
        "summary": "構成 DAG・互換・試験計画検査の部品（Kernel）は、部品の ToolLock の一覧（tool_id・contract_version・artifact_digest・ports・capabilities）と接続の辺、許された能力、fixture 案から、構成が接続できるか（connectable）と問題（重複・能力の不足・port の不在・版や向き・schema・単位の不一致・必須 port の未接続・循環）、adapter の要る辺、試験計画を事実として出す単独の部品です。AI が作った期待値は確定に数えません。inference と advice はいつも null です。",
        "problem": "部品を組んだときの不整合は、動かすまで見えません。この部品は、接続の辺の向き・版・schema・単位の食い違い、つながっていない必須の port、循環、許されていない能力を、動かす前に一覧にします。AI が作った期待値を確定した試験として数えてしまうこと、lock が無いのに「接続できる」と答えてしまうことも防ぎます。",
        "use_when": [
          "locks（0〜1,000 件）と edges（0〜10,000 件の { from: { tool_id, port, contract_version? }, to: 同じ }）から、connectable と problems を受け取りたいとき。problems が 0 件のときだけ connectable は true です。",
          "各 lock の capabilities を allowed_capabilities と照らし、許されていない能力を CAPABILITY_NOT_ALLOWED として並べたいとき。",
          "辺の両端の schema_digest が違うところを、adapter の要る辺（adapters_needed）として知りたいとき。adapter は作りません。",
          "試験計画を道具の位相順（入次数 0 の道具のうち tool_id の小さいものから）で受け取りたいとき。循環とその先で並べられない道具は unordered に並べます。",
          "fixtures（id・tool_id・input_digest・expected_digest・origin）を、道具ごとに確定（human・known_source）・未確定（ai_candidate）・食い違い（conflicting）に分けたいとき。確定の fixture の無い道具と、lock に無い道具の fixture（orphan_fixtures）も並べます。"
        ],
        "do_not_use_when": [
          "lock の digest が本物の artifact と一致するか、署名、供給者の権限、利用側の受け入れを確かめたいとき。形だけを検め、値を比べます。これらは Host・Runtime などで別に確かめます。",
          "schema の中身や互換の幅を比べたいとき。schema_digest が同じなら同じ型とみなし、違えば adapter が要るとだけ言います。",
          "単位の換算の可否や、同じ意味の別表記を判断させたいとき。単位は文字列の完全一致だけで見ます。",
          "fixture の期待値が正しいか、known_source の根拠そのものが確かかを判断させたいとき。origin を写して分けるだけです。",
          "1 つの in の port に 2 本以上の辺がつながること（fan-in）や、同じ辺の重なりを問題として出したいとき。これらは問題として出しません。contract_version の新旧の順序も判断しません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が locks・edges・allowed_capabilities・fixtures の 4 つだけを持つ素のオブジェクトでないとき INVALID_ARGUMENT、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。getter・クラスのインスタンス・穴のある配列・__proto__ のキー・記号のキー・null・関数は受け取らず、素のデータに写してから検めます。",
          "lock の形が違うとき（知らないキー、digest が sha256:<64 桁の小文字の 16 進> でない、direction の語彙の外、unit の形、port の名前や能力の重なりなど）INVALID_LOCKS で止めます。authorized などのキーは受け取らず、authorized=true で根拠を代わりにしません。",
          "辺の形が違うとき INVALID_EDGES、allowed_capabilities が id の一覧でないとき INVALID_CAPABILITIES、fixture の形が違うとき（origin の語彙の外・id の重なり・余分なキーなど）INVALID_FIXTURES で止めます。",
          "上限（入れ子 16 段・値の数 400,000・文字列 4,096・配列 100,000 要素・locks 1,000・lock ごとの port 64 と能力 64・allowed_capabilities 1,000・edges 10,000・fixtures 10,000）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
          "辺の端ごとに、道具か port が無ければ PORT_UNKNOWN、端の contract_version が lock と違えば VERSION_MISMATCH、向きが違えば（from が out、to が in でない）DIRECTION_MISMATCH を出します。両端がつながれば schema_digest の違いを SCHEMA_MISMATCH、単位の違い（不明・片側だけを含む）を UNIT_MISMATCH として出します。",
          "同じ tool_id の lock を DUPLICATE_TOOL、つながる辺の無い required の port を REQUIRED_PORT_UNCONNECTED、道具の間の矢の強連結成分（2 つ以上か自己ループ）を CYCLE として出します。lock が 0 件なら connectable を null とし、need_input（NO_LOCKS）にします。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
          "結果は構成の事実の検めで、採用・署名・発行・実行の許可ではありません。pack の発行・署名・実行環境でもありません。",
          "署名・発行・adapter の生成はしません。時計・乱数・ファイル・通信も使いません。",
          "AI の期待値（ai_candidate）は unconfirmed に並べ、確定に数えません。多数決や confidence の欄は受け取りません。確定どうしで同じ入力に違う期待値があれば、その fixture をすべて conflicting に分け、確定に数えません。",
          "ToolLock の repository・source_revision・source_path・license・publisher_evidence_ref・consumer_acceptance_ref・input_schema_digest・output_schema_digest は、形だけを検めて使いません。",
          "外の境界・状態を持たず、副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/assembly-check.js の checkAssembly({ locks, edges, allowed_capabilities, fixtures }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（connectable・problems・adapters_needed・test_plan・expectation_conflicts・tools_without_confirmed_fixture・orphan_fixtures・need_input。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-12",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "役割ごとの文脈・使わない項目 — 役割別文脈・非使用集合の部品（Kernel）",
        "summary": "役割別文脈・非使用集合の部品（Kernel）は、役割（allowed_scopes つき）ごとに、渡してよい項目の集合（projection）と、除いた項目とその理由、必要なのに入らない項目（missing_required）を出す単独の部品です。除外は derived_from を推移的にたどります。非使用の推移的な集合（non_use_closure）は参照を止める候補として出し、削除したとは言いません（deleted: false）。形の違う入力は決まったコードと位置で止めます（fail-closed）。inference と advice はいつも null です。",
        "problem": "役割に渡してはいけない情報を除くときに、使わないと決めた項目から作られた項目（派生した項目）を見落とすこと、出どころを確かめられない項目を渡してしまうこと、参照を止める候補を「削除した」と取り違えることを防ぎます。",
        "use_when": [
          "roles（0〜50 件の { role, allowed_scopes }）と items（0〜100,000 件の { id, scope, derived_from, kind }）と non_use から、役割ごとの projection と excluded を受け取りたいとき。projection と excluded で、役割ごとに項目を漏れなく重ならずに分けます。",
          "除いた理由を決まった順で全部受け取りたいとき。NON_USE → SCOPE → DERIVED_FROM_NON_USE → DERIVED_FROM_OUT_OF_SCOPE → DERIVED_FROM_NOT_ENTERED の順に、当たるものを reasons に並べ、先頭を reason にします。",
          "非使用の推移的な集合を、参照を止める候補として受け取りたいとき。non_use_closure は non_use の id と、祖先に non_use の id がある項目で、meaning は reference_stop_candidates、deleted はいつも false です。non_use にあって項目に無い id は non_use_not_in_items に出します。",
          "required（役割ごとの必要な項目の id）を渡し、projection に入らない項目とその理由を missing_required として知りたいとき。これは projection についての事実で、need_input にはしません。",
          "derived_from が入力に無い id を名指すとき、その id を items_not_entered と need_input（ITEM_NOT_ENTERED）に並べて知りたいとき。state は need_input になり、その項目は除外のままです。"
        ],
        "do_not_use_when": [
          "権限の判断・入力前の遮断・実際の参照停止や削除をさせたいとき。最終の権限は Runtime、入力前の遮断と実際の参照停止・削除は Host の役目です。この部品は渡された一覧から集合を計算するだけです。",
          "allowed_scopes や scope が正しいか（権限の真正性）を確かめたいとき。範囲の語彙は呼び出し側が決めて渡し、この部品は確かめません。",
          "方針の内容や旧方針との差を扱いたいとき。policy_version は形を検めて写すだけです。",
          "項目の種類（kind）ごとに扱いを変えたいとき、どの祖先のせいで除いたかを知りたいとき。kind は計算に使わず、理由は種類だけを出します。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "入力の形が違うとき（素のデータでない・知らないキー・足りないキー・語彙の外の値・型が違う・null・id が 1〜128 文字でない・単独のサロゲート・集合に同じ値が 2 回）INVALID_INPUT、値を調べること自体が例外になるとき（Proxy の罠・取り消された Proxy など）INSPECTION_FAILED で止めます。",
          "getter・クラスのインスタンス・穴のある配列・配列の余分なプロパティ・記号のキー・列挙できないプロパティ・null は受け取りません。読んだ値の写しだけで計算し、入力を書き換えません。",
          "役割の名前や項目の id が重なるとき DUPLICATE_ID、required が roles に無い役割を名指すとき UNKNOWN_ROLE、derived_from が循環するとき（自分を名指す項目を含む）DERIVATION_CYCLE で止めます。循環の位置は、循環の上にある項目のうち index のいちばん小さいもの（/items/<index>）です。",
          "上限（roles 50 件・allowed_scopes 1,000 件・items 100,000 件・1 つの項目の derived_from 1,000 件・derived_from の合計 1,000,000・non_use 100,000 件・required の役割 50 個と 1 つの役割の id 100,000 件）を超えたとき RESOURCE_LIMIT で止め、limit に上限の名前を入れます。",
          "除外は祖先の祖先までたどります。祖先に入力に無い id がある項目（確かめられない項目）は projection に入れません。",
          "止めるときは、最初に見つけた 1 つだけを { code, path, limit? } で返します（path は JSON Pointer）。",
          "結果を、src の補助関数を使わずに書いた独立オラクルと突き合わせるファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは事実だけです。inference と advice はいつも null です。結果は承認ではなく（approval: false）、権限も作りません（derives_authority: false）。",
          "何も削除しません。non_use_closure は参照を止める候補で、削除の記録ではありません。実際の参照停止・削除・削除の失敗は、この部品の外で起きます。",
          "権限の付与・遮断・削除の代行はしません。最終の権限・遮断・適用は Host と Runtime の役目です。",
          "非使用の記録や参照の停止を保存しません（状態を持ちません）。",
          "入出力の形はこの部品の v1 で定めたもので、使う側の受け入れはまだです。上限いっぱいの入力は単体試験で 1 回ずつ確かめただけで、時間とメモリーの測定はしていません。",
          "時計・乱数・外の境界・資格情報を持たず、ファイル・通信を使いません。副作用は NONE です。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "src/role-context.js の projectRoleContext({ roles, items, non_use, policy_version, required? }) を呼びます（例外を投げません）。返すのは { ok: true, errors: [], facts }（役割ごとの projection・excluded・missing_required と、non_use_closure・non_use_not_in_items・items_not_entered・need_input。inference・advice は null）か { ok: false, errors: [{ code, path, limit? }] } です。副作用（NONE）・状態・外の接続・資格情報は無く、利用側が用意する port もありません。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-13",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "内容・版・差分の適用 — 内容・版・差分適用の継手（Tsugite）",
        "summary": "内容・版・差分適用の継手（Tsugite）は、Kernel の型付き差分（typed-patch/v1）を、Runtime の店の 1 つの内容へ、楽観的な版（expected_revision）のもとで適用（APPLY）するか、書かずに確かめる（CHECK）ために運び、返った receipt（ContentPatchApplyReceipt）を契約どおりか検める継手（つぎて）です。店は Runtime のもので、この継手は店を作りません（fail-closed）。",
        "problem": "保存の裏で、別の人の変更を上書きしないようにします。差分を版と利用者の確認に結んで運び、受け取りと適用と不明を取り違えず、二重に適用しません。",
        "use_when": [
          "内容への変更を、基の版（expected_revision）が今の版と同じときだけ適用したいとき。版が違えば CONFLICT で、何も書きません。",
          "差分を書かずに、適用できるかだけ確かめたいとき（op CHECK、効果 NONE）。",
          "AI の案を、利用者本人がその案と差分を確かめた後だけ、採用（ADOPTED_CANDIDATE）として運びたいとき。確認は client.recordConfirmation で受け取ります。",
          "結果の分からない適用を、同じ要求の再送で解き、二重に適用しないようにしたいとき。"
        ],
        "do_not_use_when": [
          "店や版の管理そのものがほしいとき。原子的な適用・版・権限は Runtime の役目で、この継手は店を作りません。",
          "差分を作りたい・差分の中身の正しさや逆操作を確かめたいとき。それは Kernel と Runtime の役目です。",
          "CONFLICT のあとに、自動で rebase や再試行をさせたいとき。この継手は黙って rebase も再試行もしません。",
          "すぐに使える接続先がほしいとき。実の Runtime への適用は 0 回で、port は試験の代役だけです。port を実装するための Runtime 側のコマンドは、まだ設計だけです。",
          "業務の規則を組みたいとき。この継手は Kernel とデータ・サービスをつなぐアプリ基盤の継手で、業務の規則は部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "契約の版を検めます。要求の contract_pin が設定 contract_schema_digest と違うときは送りません。設定が無ければ何も送りません。",
          "作業場は設定 workspace_ids で許したものだけへ送ります。内容の hash を知っていることを権限として扱いません。",
          "利用者の確認を検めます。ADOPTED_CANDIDATE は、その案と差分（patch_digest の値）に結んだ利用者本人（origin HUMAN）の確認があるときだけ送ります。確認は照合できた APPLY の答えで使い切ります。",
          "受け取りを照合します。received の patch_digest が依頼とまったく同じで、base_revision が expected_revision と同じときだけ受け取ります。APPLIED は new_revision が expected_revision + 1 で、新しい内容の digest があるときだけです。CONFLICT は何も書いていないときだけです。",
          "受け取りの contract_pin が依頼と同じときだけ、普通の答えとして受け取ります。別の版は INCOMPATIBLE_VERSION の断りだけで、同じ pin の INCOMPATIBLE_VERSION は異常として停止します。",
          "結果の分からない APPLY（port の障害、UNKNOWN の receipt）は、同じ要求の再送で解きます（port.lookup があれば先に記録を読みます）。解けるまでほかの要求は送らず、同じ request_id で別の中身も送りません。",
          "ERABAZU の店を名乗る receipt、効果の増えた・分からない receipt、別の要求・別の作業場の receipt は受理せず、停止します（RECEIPT_REJECTED のあとは HALTED）。APPLY の効果は LOCAL_REVERSIBLE、CHECK は NONE です。"
        ],
        "what_it_does_not_do": [
          "差分の bytes が patch_digest の値になること、target.content_digest が基の版の内容の digest であること、新しい内容の digest が適用後の内容の digest であることは確かめません（Runtime の役目）。",
          "権限（REF_NOT_PERMITTED）・別の作業場（WORKSPACE_MISMATCH）・保護範囲（PROTECTED_RANGE）の判定はしません。Runtime が判定し、この継手はその答えを返します。",
          "二重に適用しないことは、Runtime の重複除きに頼ります。試験では代役がそのとおりに振る舞うことだけを確かめました。",
          "利用者の確認の記録・照合済みの記録はこの継手の中だけで、再起動で消えます。違う版を名乗る port が本当にその版を持っているかは確かめません。",
          "適用できたことは承認ではなく、内容が正しいことでも採用の承認でもありません（どの結果にも approval: false が付きます）。",
          "契約を変えません。契約の権威（schema authority）は、まだ定まっていません（NOT_SPECIFIED）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/content-patch-apply/v1（ContentPatchApplyRequest / ContentPatchApplyReceipt）でつなぎます。createContentPatchApplyClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（常に approval: false）。利用側の用意: Runtime の port（submit と任意の lookup）と cfg（contract_schema_digest・workspace_ids）。副作用: APPLY は LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-14",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "ファイルの入出力 — ファイル入出力の継手（Tsugite）",
        "summary": "ファイル入出力の継手（Tsugite）は、App で利用者が選んだファイルの handle を使って、CSV（RFC 4180・UTF-8）と JSON の取込の下見（IMPORT_PREVIEW）と書き出し（EXPORT）の依頼（FileIoRequest）を Host へ運び、返った receipt（FileIoReceipt）を契約で照合する継手（つぎて）です。下見を採用として扱わず、書き出した bytes は継手が自分で読み直して確かめます（fail-closed）。",
        "problem": "利用者が選んでいないパスや URL を読み書きしてしまうこと、取込の下見をそのまま採用と取り違えること、そして式に見える欄（= + - @ TAB CR で始まる欄）を残した CSV を書き出してしまうことを防ぎます。",
        "use_when": [
          "利用者が選んだ CSV・JSON のファイルを、取り込む前に下見したいとき。下見で受け取るのは、件数・位置と code だけの issues・digest です。",
          "CSV・JSON を新しいファイルとして書き出し、書いた bytes の長さ・sha256・欄の囲み・式への対策を、継手に読み直して確かめさせたいとき。",
          "扱う op・形式・上限・mapping を、設定で pin した範囲に限りたいとき。",
          "結果の分からない書き出しを自動で送り直さず、App が出力を確かめるまで次の書き出しを止めたいとき。"
        ],
        "do_not_use_when": [
          "取込の結果をそのまま採用（反映）したいとき。下見は採用ではなく（adopted: false）、採用は内容・版・差分適用の継手の役目です。",
          "変換の正しさ・件数の正しさ・issues の位置の正しさを確かめたいとき。それは変換・照合の部品（Kernel）と App の役目です。",
          "CSV（RFC 4180）・JSON 以外の形式や、UTF-8 以外の文字コードを扱いたいとき。",
          "既にあるファイルを上書きしたいとき。書き出しは新しいファイルだけです。",
          "パスや URL を直接指定して読み書きしたいとき。この継手は、App が admitHandle で渡した handle だけを使います。",
          "すぐに使える Host がほしいとき。実の Host・ファイルには一度も接続しておらず、port の実装・handle の出し方・設定の値は接続するときに別の関門で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定を検めます。allowed_ops・allowed_media_types に無い依頼、上限の天井（limits_ceiling。契約の 50 MiB・1,000,000 行・1024 列以下）を超える依頼、pin していない mapping、宣言した拡張子が形式に合わない依頼（.json の CSV など）は、Host に送りません。空の一覧は何も許しません。",
          "契約の版を検めます。依頼の contract_pin が設定の contract_schema_digest と違えば送りません。受け取りが名乗る版が依頼と違えば、INCOMPATIBLE_VERSION の REJECTED のほかは受け取りません。",
          "handle を検めます。App が渡していない handle、向きの違う使い方（取込元の handle を出力に使うなど）、URL・Windows のパス、一度書き出した出力の handle は使いません。",
          "依頼の形を検めます。dialect の無い CSV、dialect や式への対策のある JSON、式への対策の無い CSV の書き出し、op の欄の混在、key_fields のある書き出し、上書きを許す書き出しは送りません。同じ request_id の違う依頼も送りません（REFUSED）。",
          "receipt を照合します。request_digest の偽り、別の op の receipt、原文を変えたとする receipt、効果を増やす・不明な効果、op に合わない理由、件数の合わない receipt（rows_valid + rows_invalid が rows_read にならない）、欄の値や原文を含む issues は受理せず、停止します（HALTED）。",
          "issues の位置と順を検めます。行の範囲（CSV は 0〜rows_read、JSON は 1〜rows_read）、形式ごとの位置の欄（CSV は column、JSON は json_pointer）、code ごとの位置の有無、原文の順（同じ行では行全体の issue が先）に合わない receipt は受理しません。",
          "書き出しの後、Host が読み直した bytes（readOutput）を継手が自分で読みます。長さ・sha256 が receipt と違う、UTF-8 でない、CSV の欄が囲まれていない・式に見える欄が残る・見出しがちょうど 1 行でない、JSON がオブジェクトの配列 1 つでない、行や欄の数が合わない、付けた数（cells_escaped）を多く言うときは受理しません。",
          "Host の障害・UNKNOWN・FAILED・読み直せない出力のあとは、書き出しを自動で送り直しません。lookup で尋ねるだけで、App が outputChecked を呼ぶまで次の書き出しはしません（取込は読むだけなので続けられます）。"
        ],
        "what_it_does_not_do": [
          "下見は採用ではなく、書き出せたことは中身が正しいことではありません（どの結果にも approval: false が付きます）。",
          "取込の bytes は読みません（読むのは Host）。source_digest・preview_digest は形と対象だけを検め、計り直しません。変換の正しさ・issues の位置の正しさ・件数の正しさは確かめません。",
          "大きさ・先頭の bytes と拡張子・UTF-8・JSON の同じキーの検出は Host の役目です。継手は receipt の形と理由の組み合わせだけを検めます。",
          "書いた bytes の検めは、Host が渡した bytes が実のファイルと同じであることを前提にします。cells_escaped は上限だけを確かめます。CSV の見出しや JSON のキーが mapping の名前か、値の型は確かめません。",
          "式への対策は契約の trigger（= + - @ TAB CR）だけです。先頭の空白の後の式・全角の記号・表計算ソフトごとの別の解釈（DDE など）は範囲の外です。",
          "handle の状態と結果の分からない書き出しは、この継手の中だけにあり、再起動で消えます。outputChecked は App の申告を受け取るだけで、App が本当に確かめたかは確かめません。違う版を名乗る port が本当にその版を持つかも確かめません。",
          "特定の表計算ソフト・ファイル選択の仕組みの使い方の雛形ではありません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/file-io/v1（FileIoRequest / FileIoReceipt）でつなぎます。createFileIoClient({ cfg, port }) の admitHandle で App が handle を渡し、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: Host への port（submit、任意で lookup、書き出しには readOutput）と cfg の pin（版・op・形式・上限・mapping）。副作用: 下見は NONE、書き出しは LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-15",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "AI 実行の接続 — AI 実行接続の継手（Tsugite）",
        "summary": "AI 実行接続の継手（Tsugite）は、Runtime が固定した 1 回の AI の呼び出し（operation・slot・許可された context・予算の予約・試行の番号）を、要求（AiExecutionConnectRequest）として AI サービスへ運び、返った receipt（AiExecutionConnectReceipt）を照合する継手（つぎて）です。届いた状態・使用量・精算を分けて照合し、分からないものを 0 にせず、自動では送り直しません（fail-closed）。",
        "problem": "AI の呼び出しが時間切れや通信の喪失で終わったとき、届いたか分からないのに使用量を 0 として予約を放すことや、そのまま送り直して二重に呼び出すことを防ぎます。呼び出しの裏で見えにくい、使用量と結果の記録の食い違いを、照合で表に出します。",
        "use_when": [
          "1 回の AI の呼び出しを、配備が許す provider と model の組、許された context の参照だけ（32 件まで）で、AI サービスへ運びたいとき。",
          "時間切れ・通信の喪失を、届いたか分からない（UNKNOWN_DELIVERY）・使用量 UNKNOWN・予約は持ったまま・再試行は止める、として受け取りたいとき。",
          "送り直しを、前の試行が FAILED_CONFIRMED か NOT_EXECUTED_PROVEN と分かった後の新しい試行としてだけ出したいとき。新しい試行は、新しい request_id・operation_id・予約で、同じ予算から、1 枠 3 回までです。",
          "AI サービスの capability の宣言の digest と通信の上限（transport_limit_ms）を要求に結び、宣言と合わない呼び出しを送る前に止めたいとき。",
          "COMPLETED の reply を、bytes の大きさと sha256 を確かめてから受け取りたいとき。"
        ],
        "do_not_use_when": [
          "provider を直接呼びたいとき。provider の呼び出しは AI サービスの役目で、この継手は Runtime 側の半分です。",
          "App や部品（Kernel）から AI を呼びたいとき。この継手を呼べるのは Runtime だけです。",
          "失敗したら自動で送り直してほしいとき。この継手は自動では送り直さず、新しい試行は Runtime が決めます。",
          "予算・計画・照合の判断を任せたいとき。それは Runtime の役目です。照合の結果（verdict）は Runtime が決め、この継手は記録するだけです。",
          "すぐに使える AI サービスへの接続がほしいとき。実の AI サービス・provider には一度も接続しておらず（呼び出しは 0 回）、port の実装・設定の値は接続するときに別の関門で決めます。",
          "特定の AI provider の SDK や課金 API の使い方の雛形がほしいとき。この継手はその雛形ではありません。"
        ],
        "what_it_checks": [
          "呼ぶ側と設定を検めます。Runtime でない呼び出し、配備が許さない provider と model、設定に無い通貨、配備の固定と違う契約の版の要求は送りません。空の表は何も許しません。",
          "要求の形を検めます。生の鍵の欄、33 件以上や重複した context の参照、小数のお金、予約の tokens を超える max_output_tokens、無い・範囲（1,000〜600,000 の整数）の外の transport_limit_ms、無い・別の領域・別の provider を指す宣言の digest、要求の時刻より後の ai_service_capability_read_at を持つ要求は送りません。",
          "provider 側の文書（ai-execution-request/v1）を検めます。sha256(canonical) が provider_request_digest と違う、provider・model が要求と違う、許されていない参照・生の本文・retry_profile・鍵の実体に見える値・秘密の欄の名前・参照の形でない auth_ref を含む文書は送りません。",
          "試行の順を検めます。1 枠の 4 回目、前の試行の無い 2 回目、前の試行の結果に合わない prior_verdict、照合の前や RESPONSE_DURABLE の後の再試行、別の予算・使った予約・使った operation_id・別の slot・前の receipt と合わない digest の試行は送りません。",
          "宣言の文書（declareCapabilities() の文書）を添えたときは、sha256(canonical) が要求の digest と、transport_limit_ms の欄が要求の上限と同じかを照らします。小数・安全でない整数・穴のある配列・getter などで canonical の形に収まらない宣言は、照らさずに断ります。",
          "receipt を照合します。request_digest の偽り、別の operation・予約、時間切れなのに使用量を 0 にする・予約を放す・再試行を許す・届いたとする receipt、provider の受け取りや reply の無い COMPLETED、根拠の無い SETTLED、プロンプトや鍵をログに出したとする receipt は受理せず、停止します（HALTED）。",
          "受け取りの版と出どころを照合します。pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION、AI サービスが Runtime の記録を名乗る receipt、同じ provider の受け取りを別の operation で使う receipt（RECEIPT_CONFLICT）は受理しません。CAPABILITY_MISMATCH の後の試行は、その受け取りの current_capability_digest と同じ digest のときだけ送ります。",
          "COMPLETED は readReply で reply の bytes を読み、長さが byte_size（16,384 まで）で sha256 が reply_digest と同じときだけ受け取ります。読めなければ UNKNOWN、合わなければ受理せず停止します。"
        ],
        "what_it_does_not_do": [
          "届いたことは、中身が正しいことでも採用でもありません。どの結果にも approval: false と automatic_re_request: false が付き、再依頼は Runtime が新しい試行として決めます。",
          "reply の中身の意味・正しさ・採用は確かめません（届いたことと bytes の sha256 だけ）。照合の結果（verdict）の正しさも確かめません。",
          "使用量の値・単価・精算の正しさ、予約の額が足りるかは確かめません。予約を超えた使用量も、そのまま事実として返します。",
          "鍵は扱いません。鍵は AI サービスが持ち、参照で指します（HELD_BY_PORT）。port の例外の message も、鍵の実体を含みうるので結果に写しません。",
          "試行の記録・予約 ID・operation_id・見た provider の受け取りは、この継手の中だけにあり、再起動で消えます。再起動の後は、2 回目からの試行を送りません。",
          "答えの無い試行をいつ諦めて Runtime の記録（recordNoAnswer）にするかは決めません。この継手は時計を持たず、記録の時刻が要求の時刻＋transport_limit_ms 以上であることだけを確かめます。",
          "添えた宣言の文書や current_capability_digest が本当に AI サービスの今の宣言のものか、Runtime が本当に宣言を読み直したかは確かめません。実の AI サービスが CAPABILITY_MISMATCH で断るかは、代役で試しただけです。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/ai-execution-connect/v1 でつなぎます。createAiExecutionConnectClient({ cfg, port }) の submit(request, providerRequest, declaration?) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。Runtime は recordNoAnswer・recordReconciliation で記録します。利用側の用意: AI サービスへの port（submit・readReply、任意で lookup）と cfg の pin。副作用: 依頼は EXTERNAL_IRREVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-16",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "表示・再生の接続 — 表示・再生接続の継手（Tsugite）",
        "summary": "表示・再生接続の継手（Tsugite）は、部品（Kernel）が作った view model を、digest で固定した素材と再生設定とともに App の画面へ渡し（ViewPlaybackRequest）、App が返した receipt（ViewPlaybackReceipt）を照合する継手（つぎて）です。描くのは App で、この継手は描きません。見せた・再生したは、App の build と出力の digest で確かめます（fail-closed）。",
        "problem": "「表示した」「再生した」を裏付けなしに信じてしまうこと、同じ利用者の操作を二度命令にしてしまうこと、そして見せたことを権限の変更と取り違えることを防ぎます。",
        "use_when": [
          "view model・素材・再生設定を digest で固定して App の画面へ渡し、見せた結果を App の build と出力の digest で確かめたいとき。",
          "同じ入力（op・view model・view 契約・素材・再生設定・App の build）の出力の digest が同じであること（再現）を確かめたいとき。",
          "利用者の操作を、view（view_session_id）ごと・operation_id ごとに 1 回だけ、typed command として受け取りたいとき。",
          "欠けた素材を、理由（置き場に無い NOT_FOUND／digest が違う DIGEST_MISMATCH）と観測した digest つきで受け取りたいとき。"
        ],
        "do_not_use_when": [
          "画面に描く処理そのものがほしいとき。描くのは App で、この継手は描きません。",
          "画像を作りたいとき。画像の生成は、この継手の範囲の外です。",
          "typed command を適用したいとき。この継手は命令を送りません。適用は、内容・版・差分適用の継手か Runtime の役目です。",
          "スクリプトなど、image・audio・video・text・font 以外の media type を素材にしたいとき。",
          "すぐに使える画面・素材の置き場がほしいとき。実の App・画面・素材の置き場には一度も接続しておらず（描画・再生は 0 回）、port の実装・設定の値は接続するときに別の関門で決めます。",
          "特定の UI フレームワーク・プレイヤー・描画エンジンの雛形がほしいとき。この継手はその雛形ではありません。"
        ],
        "what_it_checks": [
          "設定と依頼を検めます。配備の固定と違う契約の版、設定（view_contracts・media_types）に無い view 契約・版・media type、スクリプトの media type、257 個以上の素材、同じ asset_ref を二度書く依頼、side effect を宣言した依頼は送りません。空の表は何も許しません。",
          "見せた結果を照合します。RENDER に PLAY で答える（またはその逆の）receipt、見せた証跡の無い receipt、依頼と違う view model・設定の digest、同じ入力なのに出力の digest が変わった receipt は受理しません。",
          "欠けた素材を照合します。一覧の無い ASSET_MISSING、依頼に無い素材や media type、理由の無い欠け、観測 digest の無い・宣言と同じ値の DIGEST_MISMATCH、digest つきの NOT_FOUND、同じ素材の欠けを 2 つ挙げる receipt は受理しません。",
          "操作を照合します。重複した operation_id の挙げ忘れ、新しい操作を重複と言う receipt、見せなかった view に操作がある receipt、RENDER に再生の操作がある receipt、1,001 個以上や知らない種類の event は受理しません。命令にするのは、新しい operation_id の event だけです。",
          "receipt の宣言を照合します。画像を作った、Runtime に UI を足した、権限を与えた、AI を呼んだとする receipt は受理しません。",
          "版と相関を照合します。request_digest の偽り、pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION、別の view（view_session_id）の receipt、対象の違う digest は受理せず、停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "見せたことは何も許しません。どの結果にも approval: false と permission_change: NONE が付きます。",
          "画面に実際に何が見えたか（画素・音）は確かめません。出力の digest は App の決定的な描画の記述の digest で、正しさは App の責任です。",
          "素材の bytes は持ちません。bytes の検め（NOT_FOUND・DIGEST_MISMATCH）は App 側の役目で、App が全部の欠けを挙げたか、観測 digest が本当に観測した値かは確かめられません（checkAssetBytes を App 側の検めとして出すだけ）。",
          "view model そのものの正しさ、view 契約の中身、typed command の中身は確かめません（digest と ref だけ）。",
          "view の記録（見た operation_id・再現の鍵）は、この継手の中だけにあり、再起動で消えます。再起動の後の重複は数えられません。",
          "App から押し出された receipt は受け取りません。submit の答えの receipt だけを照合し、同じ request_id の再送には App が同じ receipt で答えることを前提にします。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/view-playback/v1 でつなぎます。createViewPlaybackClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED のどれかを返します（どれも approval: false・permission_change: NONE）。DONE には output_digest・missing_assets・commands（typed command）などが付きます。利用側の用意: App の画面への port（submit、任意で lookup）と cfg（版・view 契約・media type）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-17",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "複数参加者の共有の版 — 複数参加者・共有版の継手（Tsugite）",
        "summary": "複数参加者・共有版の継手（Tsugite）は、参加者の event（変更の提案 PROPOSE_CHANGE・同意 CONSENT・同意の撤回 WITHDRAW_CONSENT・脱退 LEAVE・同期 SYNC）を共有 Host へ 1 つずつ運び（SharedRevisionRequest）、receipt（SharedRevisionReceipt）を照合する継手（つぎて）です。同意を（誰・どの版・その版の digest）に結び、次の版へ持ち越させません（fail-closed）。",
        "problem": "共同編集で、古い版への同意を新しい版への同意として数えてしまうこと、AI の役を人間の参加者として数えてしまうこと、切断で結果の分からない event を出し直して二重に適用させてしまうことを防ぎます。",
        "use_when": [
          "複数の参加者が共有の場で変更を提案し、同意・撤回・脱退する流れを、event 1 つずつ契約どおりに運びたいとき。",
          "同意を（誰・版・digest）に結び、変更が通ったら古い版への同意が失効として挙がることを確かめたいとき。",
          "AI の役を、認証を記録した人間が操作するものとしてだけ参加させ、同意・撤回・脱退をさせず、人間として数えたくないとき。",
          "切断で結果の分からない event を、同じ event_id のまま送り直して解きたいとき。"
        ],
        "do_not_use_when": [
          "今すぐ共有の場を動かしたいとき。共有 Host の所管は未定で、Host そのものがありません。決まるまでは、どの event も HOST_NOT_ASSIGNED で断られ、何も適用されません。",
          "共有 Host・共同編集サービス・同意管理サービスそのものがほしいとき。この継手は Host を作りません。",
          "共有の内容の差分を適用したいとき。それは内容・版・差分適用の継手（P-13）の役目です。event を並べて適用し、版・同意・重複除きを持つのは共有 Host の役目です。",
          "参加者の本人確認をさせたいとき。この継手は認証の証跡の ref を運ぶだけで、本人確認は Host の役目です。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定と参照を検めます。設定が無い、契約の pin が contract_schema_digest と違う、設定と違う host_ref、許していない作業場（workspace_ids）、..・ドライブ・制御文字・U+2028 を含む参照、知らない op・余分な欄・op に合わない本文や意図の要求は送りません。",
          "参加者を検めます。認証の証跡を記録していない人間、登録していない AI の役、操作する人の違う・自分を操作する AI の役、同じ参照を人間と AI の役に混ぜた要求、脱退した参加者（とその人が操作する AI の役）の event は送りません。",
          "AI の役の同意・撤回・脱退は送りません。AI の同意を受け付けたとする receipt、AI の役を名指す失効・脱退の通知は受理しません。",
          "版と同意を照合します。新しい版が基にした版 + 1 でない変更、版を進める変更でない event、知っている版より前に戻る版、基にした版での CONFLICT、依頼と違う（誰・版・digest）の同意の記録、同意の持ち越し、通知の無い失効は受理しません。同意は基にした版そのものへだけ送ります。",
          "撤回・脱退の答えを照合します。終わった同意を挙げない撤回、ほかの人や今でない版への同意を挙げる答え、知っている本人の同意を欠く脱退、invalidated_consents の欄の無い脱退、撤回でない op の CONSENT_NOT_FOUND は受理しません。",
          "送り直しを検めます。同じ event_id や同じ request_id で別の中身の要求、結果の分からない event があるのに新しい event_id で出し直す要求は送りません。新しい event への DUPLICATE_EVENT、再送への EVENT_ID_REUSE は受理しません。",
          "receipt を照合します。request_digest の偽り、別の op や event、op の表の外の状態、効果を増やす receipt、pin の無い・違う版の receipt、共有フォルダーの DB・暗号化 blob を共有 Host と名乗る receipt は受理せず、停止します（HALTED）。"
        ],
        "what_it_does_not_do": [
          "受け付けられた・同意が記録されたことは、内容が正しいことでも、ほかの参加者の同意でもありません（どの結果にも approval: false が付きます）。",
          "共有の内容（change_ref の中身）と状態の digest（target_digest）が正しいかは確かめません。",
          "認証の証跡は ref を運ぶだけです。本人確認そのものと、AI の役を操作する人が本当にその人かは確かめません。",
          "参加者・同意・版の記録は、この継手の中だけにあり、再起動で消えます。ほかの端末で行った同意・撤回・脱退は、通知か Host の答えでしか知りません。Host がほかの参加者全員に通知を届けたかは確かめません。",
          "実の共有 Host には一度も接続していません。試験は手元の代役と HOST_NOT_ASSIGNED の port だけで、確かめの順（pin → Host の割り当て → 参加者と event）も代役でしか確かめていません。",
          "observed_timestamp は形だけを検め、時計の正しさは確かめません。答えた port が本当にその版を持つかも確かめません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/shared-revision/v1 でつなぎます。createSharedRevisionClient({ cfg, port }) の recordAuthentication・registerAiRole で人間と AI の役を記録し、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: 共有 Host への port（所管が未定の間は createHostNotAssignedPort）と cfg（版・host_ref・workspace_ids）。副作用: SYNC は NONE、ほかは EXTERNAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-18",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "限定された操作・観測の接続 — 限定操作・観測接続の継手（Tsugite）",
        "summary": "限定操作・観測接続の継手（Tsugite）は、試行の 1 歩（観測 OBSERVE・許された 1 つの操作 ACT・初めの snapshot への戻し RESET）を App の sandbox の操作 Host へ運び（SandboxOperationRequest）、receipt（SandboxOperationReceipt）を照合する継手（つぎて）です。範囲は sandbox の中だけで、外への送信・購入・広い desktop の権限を名乗る要求も receipt も通しません（fail-closed）。",
        "problem": "試しの操作が sandbox の外に漏れること、結果の分からない操作を再クリックで埋めて二重に操作すること、そして戻せなかった環境のまま次の試行を進めてしまうことを防ぎます。",
        "use_when": [
          "試しの環境（sandbox）での観測・許された操作・初めの状態への戻しを、1 歩ずつ契約どおりに運びたいとき。",
          "操作を、pin した操作の集合の中で、その試行の最新の観測（まだ操作の基にしていないもの）に基づくときだけ送りたいとき。",
          "試行を、読み戻した snapshot が初めと同じ reset（RESET_VERIFIED）で始め、sequence を 0 から 1 ずつ進めたいとき。",
          "reset に失敗したら、人が戻したと記録する（recordHumanRestore）まで、その sandbox へ何も送らないようにしたいとき。"
        ],
        "do_not_use_when": [
          "実サイトへの送信・購入・広い desktop の操作をしたいとき。この継手の範囲は sandbox の中だけです。",
          "観測・操作・reset そのもの、時間の計測、sandbox の隔離を任せたいとき。それは App の sandbox と操作 Host の役目で、この継手は操作しません。",
          "到達の判定（goal の検査）を任せたいとき。goal の検査は操作 Host の役目です。",
          "画面操作の自動化ツール・ブラウザー操作の雛形がほしいとき。この継手はその雛形ではありません。",
          "すぐに使える sandbox がほしいとき。実の App の sandbox・操作 Host には一度も接続しておらず、試験は手元の代役（3 ページの練習用アプリ）だけです。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "設定と範囲を検めます。設定が無い、契約の pin が contract_schema_digest と違う、許していない sandbox、pin していない・digest の違う操作の集合、集合の外の対象・操作の種類・selector、sandbox の外への送信・購入・広い desktop を名乗る要求、範囲の外の上限（max_actions 1〜200・max_seconds 1〜3600）は送りません。",
          "要求の形を検めます。..・ドライブ・制御文字を含む参照、知らない op・余分な欄・op に合わない意図、知らない操作の種類、入力の無い TYPE_TEXT、対象の違う digest の要求は送りません。",
          "試行の順を検めます。RESET で始めない試行、使った trial_id、止まった sandbox・終わった試行、sequence の飛び・繰り返し、途中で条件を変えた試行は送りません。max_actions に達したら ACT を送りません。",
          "操作を検めます。観測の無い・古い観測に基づく ACT、1 つの観測での 2 つ目の ACT、同じ action_id の再送（再クリック）、ACTION_UNKNOWN の後の ACT・RESET は送りません。ACTION_UNKNOWN の後は OBSERVE だけを送ります。",
          "reset を照合します。初めと違う snapshot の RESET_VERIFIED、次の試行を許す RESET_FAILED、初めと同じ snapshot の RESET_FAILED は受理しません。",
          "receipt を照合します。request_digest の偽り、別の op・試行・sequence、外へ送信した・購入したとする receipt、効果を増やす receipt、goal の無い到達の結果、操作の記録の digest の無い ACTION_PERFORMED、数え違いや上限を超える actions_used、op の表に無い断りの理由、pin の無い・違う版の receipt は受理せず、停止します（HALTED）。",
          "port の障害で結果の分からない要求は送り直しません。lookup で記録を読むだけで、見つからなければ OBSERVE・ACT はその試行を終え、RESET は失敗と同じに扱って、人が戻すまで sandbox を止めます。"
        ],
        "what_it_does_not_do": [
          "操作した・到達したことは、操作が正しいことでも、利用者が採用したことでもありません（どの結果にも approval: false が付きます）。",
          "sandbox の隔離・購入の防止・画面の権限は確かめません。receipt の外への送信 0・購入 0 を照合するだけで、実際に防がれたかは操作 Host の役目です。",
          "観測・snapshot の中身と digest が画面と合っているか、goal の検査が正しいか、操作の記録が本当にその操作のものかは確かめません。",
          "時計を持たず、max_seconds の計時はしません。時間切れは操作 Host の LIMIT_REACHED で知ります。操作の集合の文書そのものも読まず、設定の組と digest の値で照合するだけです。",
          "試行の記録は、この継手の中だけにあり、再起動で消えます。人が sandbox を戻したことは証跡の ref で知るだけで、戻ったかは次の reset の読み戻しで確かめます。",
          "SEQUENCE_GAP・OBSERVE の TARGET_NOT_PERMITTED の断りは Host の言い分をそのまま受け取り、食い違いの原因は確かめません。答えた port が本当にその版を持つかも確かめません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/sandbox-operation/v1 でつなぎます。createSandboxOperationClient({ cfg, port }) の submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。止まった sandbox は recordHumanRestore で人の戻しを記録します。利用側の用意: 操作 Host への port（submit、任意で lookup）と cfg（版・sandbox_refs・action_sets）。副作用: OBSERVE は NONE、ACT・RESET は LOCAL_REVERSIBLE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-19",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "資料・素材の参照 — 資料・素材参照の継手（Tsugite）",
        "summary": "資料・素材参照の継手（Tsugite）は、利用者が指定したローカルの資料の 1 つの場所を、版（exact_revision）か file digest で固定し、使用条件と役の範囲を付けて資料の Host に参照を頼み（MaterialReferenceRequest）、返った receipt（MaterialReferenceReceipt）を契約で照合する継手（つぎて）です。固定して参照するか、何も返さないかのどちらかです（fail-closed）。",
        "problem": "参照している資料が途中で差し替わり、気づかないまま新しい版を使ってしまうことを防ぎます。資料の中の命令らしい文を実行の命令として扱うことと、分からない出典を推測で埋めることも防ぎます。",
        "use_when": [
          "利用者が指定したローカルの資料を、版か digest で固定し、使用条件つきで参照したいとき。",
          "原本が変わっていたら、新しい版へ黙って移らず、REVISION_CHANGED と観測した版・digest で知りたいとき。",
          "資料の決めた場所（頁・行・文字・時間の範囲など）だけを、sha256 と大きさを確かめた bytes のまま受け取りたいとき。",
          "許可の取り消し（PERMISSION_REVOKED）・参照切れ（NOT_FOUND）・位置が無い（LOCATOR_UNRESOLVED）を、それぞれの状態として受け取りたいとき。"
        ],
        "do_not_use_when": [
          "URL や別のサービスなど、外の資料を参照したいとき。外の資料は範囲の外で、能力の宣言と許可が別に要ります。",
          "資料の中身の正しさや、事実かどうかを確かめたいとき。この継手は中身を解釈せず、確かめません（NOT_VERIFIED）。",
          "原本が変わったとき、新しい版で自動で参照し直してほしいとき。新しい固定での依頼は作らず、利用者か Runtime が決めます。",
          "特定の文書管理・検索サービスの使い方の雛形がほしいとき。この継手はその雛形ではありません。",
          "すぐに使える資料の Host がほしいとき。実の資料・資料の Host には一度も接続しておらず（実の参照は 0 回）、port の実装・資料の指定の仕組み・設定の値は接続するときに別の関門で決めます。",
          "表の業務ロジックを組みたいとき。この継手は裏方・インフラの API 専用で、業務ロジックは部品（Kernel）が担います。"
        ],
        "what_it_checks": [
          "資料と版を検めます。設定の allowed_source_handles に無い資料、URL・Windows のパスの handle、LOCAL_DESIGNATED でない source_locality、contract_schema_digest と違う契約の pin の依頼は送りません。空の一覧は何も許しません。",
          "固定・位置・使用条件を検めます。固定が無い・2 つある・別の資料の digest・空の版、終わりの無い範囲・始まりが終わりより後・頁や行が 0 から、範囲つきの全体や anchor、知らない位置の種類、使用条件の無い依頼は送りません。",
          "固定の照合をします。固定と合わないのに RESOLVED とする receipt、新しい版を黙って返す receipt、中身を返す・観測の無い REVISION_CHANGED、観測と矛盾する pinned_matches、pin が合わない LOCATOR_UNRESOLVED、pin を返さない receipt は受理しません。",
          "資料はデータとして扱います。実行できるとする receipt、埋め込みの命令を許す receipt（embedded_instructions_authorized が false でない）、資料を確かめた事実とする receipt は受理しません。",
          "出典を照合します。source_identity の無い KNOWN、推測した出典の状態、名前を付けた UNKNOWN は受理しません。",
          "receipt を照合します。request_digest の偽り、使用条件や位置を返さない receipt、効果を行ったとする receipt、余分な欄、pin の無い・違う版の receipt、要求と同じ pin の INCOMPATIBLE_VERSION は受理せず、停止します（HALTED）。",
          "中身を照合します。readContent は RESOLVED の参照だけを読み、byte_size が 1 MiB を超える receipt は受理せず停止します。設定の max_content_bytes を超えるものは読まずに断ります（REFUSED）。読んだ bytes は、長さと sha256 が receipt と同じときだけ bytes のまま返し、合わなければ停止します。"
        ],
        "what_it_does_not_do": [
          "参照できたことは、確かめた事実でも、使ってよいという判断でもありません（どの結果にも approval: false が付きます）。",
          "中身の bytes を解釈しないので、資料の中の文の意味（命令らしい文かどうか）は判定しません。受け取った側がデータとして扱うことは、その側の役目です。",
          "固定と原本の比較、位置の切り出し、許可・役の範囲・使用条件の判断は、資料の Host と Runtime の役目です。この継手は receipt の形と組み合わせだけを検め、観測を固定と同じ値で偽る Host は見分けられません。",
          "その場所が本当に固定した版の中にあるか、切り出しが単位どおりか（頁の区切り方・code point の数え方・時間の位置）は確かめません。byte_size と読んだ bytes の長さ・sha256 だけを照らします。",
          "KNOWN の source_identity が本当に記録されたものか、handle の資料が本当に利用者が指定したローカルの資料かは確かめません。",
          "記録はこの継手の中だけにあり、再起動で消えます。前の参照の失効の報告は Runtime の役目です。答えた port が本当にその版を持つかも確かめません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/material-reference/v1 でつなぎます。createMaterialReferenceClient({ cfg, port }) の submit(request) で参照を頼み、readContent(request_id) で RESOLVED の中身を bytes のまま受け取ります。結果は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED です（どれも approval: false）。利用側の用意: 資料の Host への port（submit、任意で lookup、readContent には readScoped）と cfg（版・資料の一覧・中身の上限）。副作用: NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-20",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "書き出し・取り込みの往復 — Studio 往復・移行の継手（Tsugite）",
        "summary": "Studio 往復・移行の継手（Tsugite）は、候補の書き出し（EXPORT_RETURN）・書き出し先の読み取り（INSPECT_EXPORT）・受領書の検証（VERIFY_STUDIO_RECEIPT）・データ移行の preview（MIGRATION_PREVIEW）・移行の決定（MIGRATION_DECIDE）・移行の状態の読み取り（MIGRATION_STATUS）の要求（StudioRoundtripRequest）を Runtime へ運び、receipt（StudioRoundtripReceipt）を照合する継手（つぎて）です。上書きも自動のやり直しもしません（fail-closed）。",
        "problem": "作業の結果を元の場所へ戻す往復で、書き出し先にあるファイルを上書きすること、検証していない受領書や利用者が確かめていない diff で移行を決めること、結果の分からない書き出し・決定を送り直して二重に書く・決めることを防ぎます。",
        "use_when": [
          "候補を新しいファイルとして書き出し（create-new）、効果の分からない書き出しを、書き出し先の読み取りで解きたいとき。",
          "データ移行の preview を、検証済みで受け入れられた（VERIFIED・ACCEPTED）同じ base package の受領書のときだけ送りたいとき。",
          "移行の決定を、照合した preview の diff と同じ digest と、利用者本人の確認（recordConfirmation）があるときだけ送りたいとき。",
          "採用（COMMITTED）を、新しい世代（古い世代 + 1）・新しい session で、古い世代を残すときだけ受け取りたいとき。",
          "結果の分からない決定を、移行の状態（MIGRATION_STATUS）を読み、どの決定が効いたかの表で解きたいとき。"
        ],
        "do_not_use_when": [
          "受領書の署名の検証、diff の計算、移行そのものを任せたいとき。それは Runtime の役目で、この継手は送る前と受け取った後の検めだけをします。",
          "書き出し先のファイルを置き換えたいとき。書き出しは新しいファイルだけで、書き出し先に何かがあれば Runtime は何も書かずに DESTINATION_EXISTS で答えます。",
          "受領書の検証や移行の結果を、package の実行の許可や採用の承認として使いたいとき。",
          "すぐに動く往復がほしいとき。相手の Runtime 側の口（取り込み・移行・書き出し先の読み取りなど）はまだ設計だけか無く、port は試験の代役だけです。実の書き出し・読み取り・検証・移行は 0 回です。",
          "業務の規則を組みたいとき。この継手はアプリ基盤の継手で、業務の規則は部品（Kernel）と構成で担います。"
        ],
        "what_it_checks": [
          "設定と要求の形を検めます。設定が無い、契約の pin が contract_schema_digest と違う、destination_handle_refs に無い書き出し先（読み取りも）、..・ドライブ・制御文字・U+2028 を含む参照、知らない op・余分な欄・ほかの op の欄、自動で適用する書き出し（automatic_apply）、空や繰り返しの候補の要求は送りません。",
          "preview と決定を検めます。検証済みで受け入れられた受領書の無い・別の base package・検証し直して通らなかった preview、知らない移行・別の diff の digest・確認の無い決定、HUMAN でない・別の決定や移行や diff に結んだ・使った確認、同じ preview の 2 回目の決定は送りません。",
          "受領書の検証の答えを照合します。UNVERIFIED・MISMATCH・受け入れられていない受領書を preview に進めるとする答え、発行 package の digest の無い ACCEPTED、studio_receipt_id の無い VERIFIED は受理しません。",
          "決定と移行の答えを照合します。ADOPT に DECLINED・DECLINE に COMMITTED で答える receipt、decision を返さない・入れ替える receipt、古い世代を落とす・同じ世代・世代の飛ぶ COMMITTED、別の移行の receipt は受理しません。",
          "書き出し先の答えを照合します。found_bundle の無い・形の違う・自動で適用する EXPORT_PRESENT、found_bundle の付いたほかの状態、digest の無い・対象の違う EXPORT_UNREADABLE、EXPORT_RETURN でない op の DESTINATION_EXISTS、効果を名乗る読み取りや断りは受理しません。",
          "書き出し・決定の結果不明は送り直しません。書き出しは記録（lookup）か書き出し先の読み取りで、決定は記録か移行の状態で解き、解けるまでほかは送りません。書き出し直すのは EXPORT_ABSENT のときだけで、別の bundle や読めないファイルがある書き出し先へは何も書きません。",
          "receipt を照合します。request_digest の偽り、別の op の receipt、op の表の外の状態、原本を編集・発行・再署名したとする receipt、受領書だけで実行を許すとする receipt、効果の増えた receipt、op に合わない断りの理由、pin の無い・違う版の receipt は受理せず、停止します（HALTED）。",
          "送った決定と違う決定が効いたと移行の状態が示したら、異常とせず事実として other_decision_took_effect: true を添えて返します。その移行はもう決めません。"
        ],
        "what_it_does_not_do": [
          "受領書を検証できた・preview に進める・移行できたことは、package の実行の許可でも採用の承認でもありません（どの結果にも approval: false が付きます）。",
          "受領書の署名の検証・信頼する鍵・bundle や diff の bytes の sha256・移行そのものは確かめません。receipt の値を照合するだけです。",
          "found_bundle が本当に書き出し先のファイルから読んだ中身か、読み取りが何も書かなかったか、create-new が本当に置き換えないかは確かめません。create-new に背く Runtime から守れるのは、読んで別の bundle・読めないファイルがあると知った書き出し先だけで、それも再起動までです。",
          "人の判断が要る答え（needs_human_decision: true）の後に、人が何をしたかは知りません。止めて人に尋ねるのは呼ぶ側です。",
          "照合した受領書・preview・利用者の確認・書き出し先の記録は、この継手の中だけにあり、再起動で消えます。",
          "COMMITTED などの状態が、この継手の送った決定の効果か、別の送り手の同じ決定の効果かは見分けられません。",
          "書き出し・決定の結果不明が解けないとき（記録が無い、状態が CREATED・PREVIEWING のまま、読み取りが断られる）は、UNKNOWN のまま止まり、人の確認を待ちます。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。契約も変えません（schema authority は NOT_SPECIFIED）。"
        ],
        "integration": "契約 erabazu.candidate/certified-tsugite/studio-roundtrip/v1 でつなぎます。createStudioRoundtripClient({ cfg, port }) の recordConfirmation で利用者の確認を受け取り、submit(request) は DONE・ALREADY_DONE・REFUSED・UNKNOWN・RECEIPT_REJECTED・HALTED を返します（どれも approval: false）。利用側の用意: Runtime への port（submit、任意で lookup）と cfg（版・destination_handle_refs）。副作用: 書き出し・preview・採用は LOCAL_REVERSIBLE、読み取り・検証・辞退・状態は NONE。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-21",
        "category": "P/アプリ基盤",
        "kind": "Component",
        "title": "型つきの答えの取り込み — 型つきの答えの取り込みの部品（Component）",
        "summary": "型つきの答えの取り込みの部品（Component）は、AI の答えの文字列を、決まった封筒（status・binding・枝ごとの中身）と使う側が渡す判断の schema で検め、採用の候補の下書き（PROPOSAL_CANDIDATE）・HOLD・OUT_OF_SCOPE に分ける部品です。答えの読み取りと型の検めは部品(Kernel) P-01 に port で頼み、枝の決まりと binding の 8 つのキーの照合はこの部品が行います。PROPOSE の答えでも採用しません（NOT_ADOPTED）。",
        "problem": "AI の「提案」の裏で、その答えが古い入力に対するものでないかは見えません。この部品は、型の違う答え・古い入力の答え・読めない port の答えを候補にせず（fail-closed）、binding の 8 つのキーが期待と違う答えを STALE_OR_UNBOUND で止めます。モデルが答えの中で「valid」などと申告しても、それを根拠にしません。",
        "use_when": [
          "AI の答えの文字列（answer_text、4,194,304 の符号単位まで）を、封筒 { status, binding, proposal | questions | reason } と使う側の判断の schema（proposal_schema）で検め、PROPOSAL_CANDIDATE・HOLD・OUT_OF_SCOPE のどれかとして受け取りたいとき（intake）。",
          "答えが、呼び出し側の期待する条件（product_id・case_id・role・input_revision・input_digest・selection_digest・profile_revision・request_id の 8 つ）の上のものかを、UTF-16 の符号単位の完全一致で照らしたいとき。違えば STALE_OR_UNBOUND で、違うキーを決まった順で detail.mismatched に返します。",
          "status ごとの中身の決まり（PROPOSE は proposal だけ、HOLD は questions だけ、OUT_OF_SCOPE は reason だけ）を、P-01 が合うと答えた後に確かめたいとき。違えば ANSWER_NOT_TYPED（stage: branch）です。",
          "型の違いを、どの段で外れたか（detail.stage: json・envelope・proposal・branch）と、P-01 の issue（位置とコード、20 件まで）で受け取りたいとき。",
          "どの判断の schema で検めたかを、封筒と判断の schema を 1 つにした schema の digest（schema_digest）で結果に添えたいとき。",
          "例外を投げず、入力を書き換えず、時計・乱数・状態を持たない処理として、同じ答えと同じ P-01 の答えから同じ結果を受け取りたいとき。"
        ],
        "do_not_use_when": [
          "PROPOSAL_CANDIDATE を、採用・保存・外への適用として使いたいとき。PROPOSAL_CANDIDATE は「型と binding が合う下書き」で、内容が正しいこと・採用・保存の許可ではありません。採用の記録は使う側の役目です。",
          "proposal の内容が正しいか、答えの根拠（basis）が確かかを確かめたいとき。この部品が見るのは型と binding だけです。根拠の検めは使う側の役目です。",
          "安全な整数を超える数を、数として正確に受け取りたいとき。proposal は JSON.parse で読んだ値で返すため、そうした数は丸まりえます。",
          "Promise を返す非同期の port を P-01 の代わりにつなぎたいとき。非同期の port には対応せず、PORT_FAILED で止めます。",
          "既にある判断の schema の候補の形へ、この封筒の答えを移したいとき。移し替えはこの部品の外の別の adapter の役目で、既存の schema は変えません。",
          "本人の確認や、答えを残すことまで任せたいとき。この部品は答えを残さず、本人の確認もしません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "options が素のオブジェクトでない・知らない名前や getter があるとき INVALID_OPTIONS、contract_check か schema_digest が関数でないとき PORT_MISSING、schema_id が 1〜256 文字の文字列でないとき INVALID_SCHEMA_ID、proposal_schema が素の JSON のオブジェクトでない（クラスのインスタンス・getter・穴・有限でない数・入れ子 64 段を超える）とき INVALID_PROPOSAL_SCHEMA で止めます。",
          "封筒と判断の schema を 1 つにした schema を P-01 が読めない、または信頼しない（部分集合の外のキーワードなど）とき、PROPOSAL_SCHEMA_NOT_TRUSTED で止めます。",
          "P-01 が投げた、答えの形が読めない、state が done でない、重複するキーを見ていない、合うと言った答えが JSON・封筒として読めない（偽りの答え）とき、通さずに PORT_FAILED で止めます。",
          "answer_text が文字列でない（箱の文字列を含む）・4,194,304 の符号単位を超えるとき INVALID_ANSWER、expected_binding が 8 つのキーをちょうど 1〜256 文字の文字列で持つ素のオブジェクトでないとき INVALID_EXPECTED_BINDING（detail.path）で止めます。",
          "自然文・壊れた JSON・単独のサロゲート・UTF-8 で 1 MiB を超える答え・重複するキー・封筒の外のキー・知らない status・判断の schema の外の proposal・枝の中身の欠けや混ざりは、ANSWER_NOT_TYPED で止めます。",
          "型の検めが通った後、binding のキーが 1 つでも期待と違えば（大文字小文字の違いを含む）STALE_OR_UNBOUND で止めます。値を調べること自体が例外になるとき（Proxy の罠など）は INSPECTION_FAILED です。getter は呼びません。",
          "結果を、別に書いた判定の手本と突き合わせる 1,000,000 回のファズ（P-01 の代役のランダムな故障を含む）で、不一致と fail-open が 0 であることを確かめました。本物の P-01 との結線も確かめました。"
        ],
        "what_it_does_not_do": [
          "どの結果にも adoption: NOT_ADOPTED・approval: false・authority: NOT_DERIVED・claim: NOT_DERIVED が付きます。PROPOSE の答えでも採用・保存・外への適用はせず、結果は承認ではなく（result_is_approval: false）、権限も作りません（derives_authority: false）。",
          "合う・合わないの判断は P-01 のままで、この部品は P-01 の答えを読むだけです。主張の正しさや根拠を格上げせず、proposal の中身はそのまま返します。",
          "P-01 が正しく働くことは、P-01 の検査に任せています。ここで確かめたのは、本物の P-01 との結線までです。",
          "binding に試行（attempt）の ID は入れません。AI は実行側の試行を知らないため、要求と試行の対応はこの部品の外で扱います。",
          "判断の schema の中身・採用・保存・本人の確認は、この部品の外で使う側が決めます。答えも残しません（状態は設定だけで、凍結されています）。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定の AI の出力の parser の使い方の雛形ではありません。"
        ],
        "integration": "src/intake.js の createTypedAnswerIntake({ contract_check, schema_digest, schema_id, proposal_schema }) で instance を作り、instance.intake(answer_text, expected_binding) を呼びます。port には P-01 の checkContract・schemaDigest（同期）を渡します。返すのは { ok: true, errors: [], status, binding, proposal, questions, reason, schema_digest } か { ok: false, errors: [{ code, detail? }] } で、投げません。副作用・状態・資格情報は無く、依存は組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      },
      {
        "id": "P-22",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "採用の版の台帳 — 採用の版の台帳の部品（Kernel）",
        "summary": "採用の版の台帳の部品（Kernel）は、版つきの項目の「候補 → 人の確認 → 採用 → 旧採用・失効」の移り変わりと確認の記録を、渡された台帳の上で 1 操作ずつ当て（applyAdoption）、今の条件と影響の入力から項目ごとの事実を返す（reviewAdoption）単独の部品です。新しい版（訂正を含む）は必ず候補で入り、採用はその版に合う人の確認があるときだけです。旧版・失効したもの・履歴は消しません。inference と advice はいつも null です。",
        "problem": "「採用中」の裏で、誰がどの版を確かめたかは見えません。この部品は、AI が確認・採用・失効すること、訂正を自動で採用すること、古い版の送り直しをより新しい採用の上に採用すること、旧採用や履歴を消すこと、つじつまの合わない台帳を使うことを防ぎます。",
        "use_when": [
          "新しい版を、その項目の今までのどの版より大きい版として、候補（CANDIDATE）で台帳に足したいとき（PROPOSE）。source は PERSON か AI で、AI の提案は候補の欄に入り、確認の欄とは別です。",
          "人（actor_kind: PERSON）の確認を、候補と中身の digest・binding のどの欄も同じときだけ記録したいとき（CONFIRM）。同じ digest でも binding の欄が違えば別のものとして扱います。",
          "人の確認がある候補だけを採用として記録し、前の採用を旧採用（SUPERSEDED）にしたいとき（ADOPT）。候補か採用を失効（REVOKED）にしたいとき（REVOKE、reason_ref つき）。",
          "台帳の版（expected_ledger_revision）を添えて操作を当て、台帳が先に進んでいたら何も当てずに LEDGER_CONFLICT で受け取りたいとき。",
          "採用の版の binding の欄ごとに今の条件（context）と比べ、違う欄（changed）・今の値が無い欄（unknown）と、再確認の状態（CURRENT・RECONFIRM_REQUIRED・RECONFIRM_UNKNOWN・NOT_ADOPTED）を項目ごとに受け取りたいとき（reviewAdoption）。",
          "使う側が別に計算した影響（AFFECTED・NOT_AFFECTED・UNKNOWN）を impacts として渡し、項目ごとの impact（NOT_AFFECTED・REVOCATION_CANDIDATE・IMPACT_UNKNOWN・IMPACT_NOT_PROVIDED・NOT_ADOPTED）として写して受け取りたいとき。",
          "時計・乱数・環境の locale を使わず（時刻は呼び出し側が at で渡す）、例外を投げず、入力を書き換えずに、同じ入力から同じ事実が要るとき。"
        ],
        "do_not_use_when": [
          "台帳を保存したいとき。結果の新しい台帳は保存前の候補（committed: false）で、保存と原子的な比較は使う側の役目です。",
          "actor_ref・actor_kind が本当にその人かを確かめたいとき。本人の確かめ（認証）はせず、呼び出し側の申告として記録します。",
          "影響を計算させたい、影響の入力が正しいかを確かめたいとき。影響の入力は写すだけで、渡されていない影響は「影響なし」にせず IMPACT_NOT_PROVIDED にします。",
          "失効を実行させたいとき。REVOCATION_CANDIDATE は影響の入力の写しで、失効の実行ではありません。REVOKE の操作は人の操作として渡されたときだけ当てます。",
          "binding の欄の意味（どの欄が材料の版か）を部品に決めさせたいとき。欄の名前は使う側が決めます。",
          "上限（entries・confirmations・impacts 10,000 件、events 100,000 件）を超える台帳や、大きな台帳での速さが要るとき。確認の照合は件数の 2 乗で、大きな台帳での速さは確かめていません。",
          "Node.js v24.21.0 のほかの版で使うとき。ほかの版は確かめていません（NOT_VERIFIED）。"
        ],
        "what_it_checks": [
          "引数が素のオブジェクトでない・知らない名前や getter がある・欄が無いとき INVALID_INPUT、台帳の形が違う（schema・版の数・digest・binding・source・state、確認の actor_kind が PERSON でない、出来事の op など）とき INVALID_LEDGER、件数の上限を超えるとき TOO_MANY_ENTRIES で止めます。",
          "台帳の内側のつじつま（ledger_revision と出来事の数、seq の飛び、同じ項目と版の組、採用が 2 つ、在る候補と合わない確認、確認の無い採用・旧採用）が合わないとき、LEDGER_INCONSISTENT で止め、その台帳を使いません。",
          "操作の形が違うとき（知らない op、その op に無い名前・getter、欠け、空の item_id、0・小数・文字列・安全でない整数の版、大文字の digest、binding の欄の名前・値・数の違い、知らない source、actor_ref・at の欠け）、INVALID_OPERATION で止めます。",
          "CONFIRM・ADOPT・REVOKE の actor_kind が PERSON でないとき CONFIRMER_NOT_PERSON、CONFIRM の digest か binding の欄が候補と違うとき CONFIRMATION_MISMATCH（path は最初の違い）、ADOPT の版に人の確認が無いとき NOT_CONFIRMED で止めます。",
          "PROPOSE の版が今までの版より大きくないとき REVISION_NOT_NEWER、項目と版の組が無いとき UNKNOWN_ENTRY、CONFIRM・ADOPT の相手が候補でないとき NOT_A_CANDIDATE、より新しい採用があるとき STALE_REVISION、REVOKE の相手が旧採用か失効済みのとき NOT_REVOCABLE で止めます。",
          "context の形が違うとき INVALID_CONTEXT、impacts の形が違う・同じ版が 2 回あるとき INVALID_IMPACTS、値を調べること自体が例外になるとき（Proxy の罠など）INSPECTION_FAILED で止めます。止めるときは最初の 1 件のコードと位置（path。JSON Pointer）を返します。",
          "結果を、線形の探索だけで書いた状態のモデルと 1 操作ずつ突き合わせる 1,000,000 回のファズで、不一致と fail-open が 0 であることを確かめました。"
        ],
        "what_it_does_not_do": [
          "返すのは台帳の移り変わりと項目ごとの事実だけです。inference と advice はいつも null で、判断をしません。ADOPTED は「人が確認した版を採用として記録した」事実で、内容の正しさや承認ではありません（result_is_approval: false）。権限も作りません（derives_authority: false）。",
          "訂正（新しい版）を自動で採用しません。どの操作も、項目・確認・出来事を消しません。",
          "より新しい採用が失効した後は、古い確認済みの候補を人が採用し直せます。STALE_REVISION になるのは、より新しい ADOPTED があるときだけです。",
          "台帳の保存・原子的な比較・本人の確かめ・失効の実行・影響の計算は、この部品の外で使う側が行います。影響の計算の結果は impacts として受け取り、意味を作り変えません。",
          "外の境界・状態・時計・乱数を持ちません。副作用は NONE で、資格情報も使いません。",
          "工房の検査に通ったことは、認証ではありません。出荷してよいかは評価しておらず、公開もしておらず、権限も与えません。",
          "特定のワークフローエンジンの使い方の雛形ではありません。"
        ],
        "integration": "src/adoption-ledger.js の applyAdoption({ ledger, operation }) と reviewAdoption({ ledger, context, impacts }) を呼びます（どちらも投げません）。空の台帳は schema \"erabazu.kernel/adoption-ledger/v1\"・ledger_revision 0・空の配列 3 つです。返すのは { ok: true, errors: [], facts }（新しい台帳と committed: false と transition、または items。inference・advice は null）か { ok: false, errors: [{ code, path }] } です。port は無く、依存は Node.js の組み込みだけです。",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "工房の技術検査PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "保存された検査記録の結果です。実サービスへの接続や出荷可否の評価とは別です。"
        }
      }
    ],
    "en": [
      {
        "id": "A-01",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Token verification — Token Verify Tsugite",
        "summary": "The Token Verify Tsugite (継手, tsugite) checks the claims of a token received from a backstage authentication platform (OIDC/OAuth, JWT). If any one of alg, aud, iss, exp or the flag for whether the signature was verified (signature_valid) does not match, it does not let the token through (fail-closed). It does not verify the signature itself; the caller verifies it and passes signature_valid.",
        "problem": "It prevents a login from ending at \"I could log in\" while what the token verification actually checked stays invisible. It is a check that always stops, at the decision step, tokens with alg: none or an alg that is not allowed, an aud meant for another party, an issuer that is not pinned, or an expired token.",
        "use_when": [
          "You want to check the alg, aud, iss and exp of an OIDC/OAuth token against rules you have set.",
          "You want to reliably reject tokens with alg: none or an alg that is not allowed (for example, HS256).",
          "You verify the signature on your own side (with a pinned JWKS or key), and want a pure function with no network access to decide on that result together with the claims.",
          "You want to pass the result of the token validity decision on to the Kernels for authorization decisions (policy-facts) or for audit and provenance (token-facts)."
        ],
        "do_not_use_when": [
          "You want the token's signature itself to be verified. This Tsugite does not verify signatures. It only receives signature_valid (a boolean) from the caller and rejects the token if that value does not say verified.",
          "You also want it to fetch the IdP or JWKS. Fetching is not included.",
          "You want nbf, iat or kid checked, or you want to handle tokens whose aud is an array. aud is matched as a single value; an array aud is not handled.",
          "You want to decide authorization (who is allowed to do what). Authorization decisions are passed to the policy-facts Kernel.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks alg. alg: none and any alg not in the allow list (allowed_algs), for example HS256, are rejected.",
          "It checks aud. An aud that does not match the allowed aud (allowed_aud), i.e. aud confusion, is rejected.",
          "It checks iss. Any issuer other than the pinned iss (allowed_iss) is rejected.",
          "It checks exp. Tokens past their expiry, including the grace period (leeway_seconds, default 0), and tokens without exp are rejected.",
          "It checks signature_valid. A token whose signature is not verified (signature_valid is not true) is rejected.",
          "If any one check fails, it returns { ok: false, errors: [...] } and does not let the token through (fail-closed)."
        ],
        "what_it_does_not_do": [
          "It does not actually verify the token's signature (verification with a pinned JWKS or key). signature_valid is a value the caller verifies and passes in, and this Tsugite trusts that value when deciding.",
          "It does not fetch the IdP or JWKS. It is a decision-only part with no network access, and it has no port.",
          "It does not check nbf, iat or kid. It does not handle an array aud either.",
          "It checks only five claims: alg, aud, iss, exp and signature_valid. The \"revocation\" in the catalog one-liner is handled in this Tsugite as rejecting expired tokens by exp (including leeway).",
          "It is not connected to an actual JWKS or key inside this part. They are pinned at deployment.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use an IdP."
        ],
        "integration": "Input: call verifyClaims(cfg, claims, now) in src/guards.js (a pure function with no network access). Pass cfg with allowed_algs, allowed_aud, allowed_iss and leeway_seconds, claims as { alg, aud, iss, exp, signature_valid }, and now as the verification time (in seconds). Returns: { ok: true, errors: [] } or { ok: false, errors: [...] }. User provides: verification of the token's signature (with a pinned JWKS or key) and the resulting signature_valid, and fetching of the IdP and JWKS. Side effect: none; there is no port and no external side effect.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-02",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Authorization decision reference — Authentication and Authorization Extension Tsugite",
        "summary": "The Authentication and Authorization Extension Tsugite (継手, tsugite), for A-02, takes a permission decision issued by an external decision point as a reference, checks it against the pinned permission policies, carries it to the platform, and matches the returned receipt against the contract. It does not evaluate permissions (RBAC/ABAC). The decision is an external one, and receiving PERMIT does not derive any permission authority. If any one check fails, it does not send and does not accept (fail-closed).",
        "problem": "When a permission decision from an external decision point is handed over, this Tsugite prevents carrying a decision under a policy that is not pinned, deriving permission authority from a receipt that presents itself as a decision, and mistaking receipt of a decision for approval.",
        "use_when": [
          "You want to carry a permission decision issued by an external decision point to the platform as a reference (decision_ref), and only within the pinned permission policies (policy_refs).",
          "You want to match the result by request_id, request_digest and references before accepting it. The DONE result carries decision, decision_ref and permission_authority.",
          "You want the handover of a decision to cause no external effect. The side effect of a permission decision reference is NONE.",
          "Even when a platform failure leaves the result unknown, you want to avoid sending the same request_id twice and resolve it with lookup."
        ],
        "do_not_use_when": [
          "You want it to evaluate, with RBAC/ABAC, whether this person may do this. This Tsugite carries the decision of an external decision point as a reference; it does not include any function for evaluating permissions.",
          "You want to use a received PERMIT as permission authority. Receiving PERMIT does not derive permission authority, and a receipt that claims to have derived permission_authority is not accepted.",
          "You want to confirm that the decision is correct. It does not check the permission decision itself (that is the role of the external decision and the platform).",
          "You want a decision point you can use right away. No actual platform (which decision point) is included, and it is undecided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for A-02 alone. Under the contract, this Tsugite serves A-02, A-04, A-05, A-06 and A-07 as one part, and the tests and inspection are shared. A-01 is served by the token-verify Tsugite, and A-03 and A-08 by the session Tsugite."
        ],
        "what_it_checks": [
          "It checks the policy. It does not send a request under a permission policy that is not pinned. An empty list allows nothing.",
          "It checks subject references and the form of references. It does not send a subject reference in the form of an email address or phone number, a reference containing a path or .. (path traversal), or extra fields.",
          "A receipt that claims to have derived permission authority is not accepted.",
          "If the receipt's request_id, request_digest or references do not match the request, the receipt is not accepted. A receipt with a spoofed request_digest is not accepted either.",
          "A receipt that increases the external effect, or whose effect is UNKNOWN, is not accepted. If a receipt that cannot be accepted arrives, the client stops (HALTED).",
          "It does not send the same request_id twice. A platform failure is treated as UNKNOWN, and resending is resolved with lookup."
        ],
        "what_it_does_not_do": [
          "It does not evaluate permissions. This Tsugite carries the decision of an external decision point as a reference and does not include any RBAC/ABAC evaluation function. The permission decision is the role of the external decision point, and the facts of the permission policy are the role of the Kernel (policy-facts).",
          "PERMIT is not permission authority. It derives no permission or authority from having received a decision.",
          "Receiving a decision is not approval (approval: false).",
          "It does not include an actual platform (which decision point). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not recognize every direct identifier. Detection covers only the typical forms of email addresses and phone numbers.",
          "It is not a template for how to use a specific IdP or decision point. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/identity-ext/v1. Input: pass an AuthzDecisionRefRequest to submit(request) on createIdentityExtClient({ cfg, port }); cfg requires all six pinned lists (policy_refs and the others). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries decision, decision_ref and permission_authority in result. Every result has approval: false. User provides: a port to the platform (submit, and optionally lookup). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-03",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Session management — Session and Revocation Propagation Tsugite",
        "summary": "The Session and Revocation Propagation Tsugite (継手, tsugite) checks expiry, concurrency and known state before sending OPEN, RESUME, TERMINATE and REVOKE operations to a session store, and matches the store's receipt (SessionReceipt) before advancing the session's state. If anything does not match, it does not send and does not treat the session as usable (fail-closed).",
        "problem": "Being logged in is often taken for granted, yet sessions past their expiry, sessions over the concurrency limit, and sessions that should have been revoked or terminated can stay in use. This Tsugite checks session issuing, resuming, terminating and revoking before sending, and does not treat a session as usable unless the store's receipt can be matched.",
        "use_when": [
          "You want to issue, resume, terminate and revoke sessions against an external session store, and match the result before advancing the state.",
          "You want the maximum session length (max_session_seconds) and the limit on sessions in use at the same time (max_concurrency) always enforced at open time.",
          "You want a session to be judged usable at a given time only when all of these hold: matched, open, within expiry, and no unsettled operation.",
          "You want a session treated as unusable until resolved when a store failure leaves the result unknown."
        ],
        "do_not_use_when": [
          "You want the existence of a session to stand in for identity verification or authority. A session is neither identity verification nor authority (authority: NOT_DERIVED, approval: false).",
          "You want to verify the token itself. That is the role of A-01 (token-verify-tsugite); this Tsugite handles only session operations and revocation propagation.",
          "You want a session store you can use right away. No actual session store is included, and which one to connect to has not been decided.",
          "You want the session expiry or concurrency limit itself to be decided. Deciding is the role of the Kernels (session-facts, policy-facts, timestamp-facts); this Tsugite only receives the limits and state and checks them.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks concurrency. An OPEN that would exceed the limit on sessions in use at the same time (max_concurrency) is not sent.",
          "It checks expiry and time. It rejects expiry rollback and time rollback. OPEN must carry an expiry (expiry) and a concurrency limit (concurrency_limit), and operations other than OPEN may not change expiry or concurrency.",
          "It rejects resuming a revoked or terminated session. If the store returns RESUMED after a revocation, it does not accept it and stops.",
          "It matches the receipt (SessionReceipt). If the state contradicts the operation (session_state is determined by op; NONE and UNKNOWN are not matched either), the receipt is for another session, or request_digest is spoofed, it is not accepted and the client goes to HALTED from then on.",
          "It rejects receipts that bring in authority or identity verification. A receipt that derives authority, or that marks identity as verified (VERIFIED) without a verifier, is not accepted.",
          "It rejects requests that place a raw secret next to a SecretRef.",
          "A revocation (REVOKE) receipt must carry a propagation reference, and the revocation stays \"incomplete\" (REVOKED_PROPAGATION_INCOMPLETE) until it has reached every pinned propagation target with evidence. Details are covered in A-08 (revocation propagation).",
          "A store failure is treated as UNKNOWN, and that session is treated as unusable until resolved."
        ],
        "what_it_does_not_do": [
          "A session is neither identity verification nor authority. Every result carries authority: NOT_DERIVED and approval: false.",
          "It does not verify tokens. That is the role of A-01 (token-verify-tsugite).",
          "It does not include an actual session store or propagation targets (each service's revocation intake). Which to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It does not decide session limits or rules. Deciding is the role of the Kernels (session-facts, policy-facts, timestamp-facts); this Tsugite only checks the limits and state it receives.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED). Note that some points, such as the fields required for OPEN, are stricter than the contract.",
          "A-03 and A-08 are one part under the contract. Each ID's row (A-03 and A-08) points to this part.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a session store."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1. Input: pass a SessionOpRequest (OPEN, RESUME, TERMINATE or REVOKE) to apply(request) on createSessionClient({ cfg, port }); isUsable(session_ref, now) decides whether a session can be used. Returns: one of APPLIED, REFUSED, UNKNOWN, RECEIPT_REJECTED, HALTED, REVOKED_EVERYWHERE or REVOKED_PROPAGATION_INCOMPLETE. Every result has approval: false. User provides: the store and propagation ports (apply and propagation). Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-04",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Multi-factor and step-up authentication (carries the challenge and the verification) — Authentication and Authorization Extension Tsugite",
        "summary": "The Authentication and Authorization Extension Tsugite (継手, tsugite), for A-04, checks requests for an additional-authentication challenge (CHALLENGE) and its verification (VERIFY) against the pinned factors and credential references, carries them to the platform, and matches the returned receipt against the contract. Verification is done only against a challenge that this Tsugite sent to the same subject. Satisfying additional authentication is not permission or authority. If any one check fails, it does not send and does not accept (fail-closed).",
        "problem": "When additional authentication is inserted just before an important operation, this Tsugite prevents verifying against a challenge that was never sent or a challenge for another subject, carrying token-shaped values as they are, and mistaking satisfied additional authentication for permission.",
        "use_when": [
          "You want to send an additional-authentication challenge to the platform only just before an important operation, and verify the answer.",
          "You want to keep additional-authentication factors within the list of allowed factors (allowed_factors).",
          "You want verification limited to a challenge that this Tsugite sent to the same subject and matched as CHALLENGED. The states are CHALLENGE to CHALLENGED, FAILED or UNKNOWN, and VERIFY to SATISFIED, FAILED or UNKNOWN.",
          "You want to match the result by request_id, request_digest and references before accepting it. The DONE result carries step_up_state, satisfied and authorization.",
          "Even when a platform failure leaves the result unknown, you want to avoid sending the same request_id twice and resolve it with lookup."
        ],
        "do_not_use_when": [
          "You want to use satisfied additional authentication as permission for an operation. Satisfying it is not permission or authority, and a receipt that claims to have derived permission is not accepted.",
          "You want to confirm that the additional authentication is correct or that a credential is valid. It does not check these (that is the role of the external decision and the platform).",
          "You want to carry the values of secrets or tokens themselves. The bytes of secrets and tokens are not carried; only references are.",
          "You want an additional-authentication platform you can use right away. No actual platform (which IdP) is included, and it is undecided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for A-04 alone. Under the contract, this Tsugite serves A-02, A-04, A-05, A-06 and A-07 as one part, and the tests and inspection are shared. A-01 is served by the token-verify Tsugite, and A-03 and A-08 by the session Tsugite."
        ],
        "what_it_checks": [
          "It checks factors. It does not send a request for a factor that is not in the allowed list or not in the contract. An empty list allows nothing.",
          "It checks what is being verified. It does not send a verification against a challenge this Tsugite did not send, a challenge to another subject, or a challenge that failed (REFUSED).",
          "It checks credential references. It does not send a request for a credential that is not pinned. If a credential reference or assertion_ref has the shape of bytes or a token, it is not sent even if it is pinned.",
          "It checks subject references and the form of references. It does not send a subject reference in the form of an email address or phone number, a reference containing a path or .. (path traversal), or extra fields.",
          "A receipt that claims to have derived permission from additional authentication is not accepted.",
          "A receipt whose request_id, request_digest or references do not match the request, that increases the external effect, or whose effect is UNKNOWN is not accepted. If a receipt that cannot be accepted arrives, the client stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "Satisfying additional authentication is not permission or authority. The permission decision is the role of the external decision point and of people and organizations; this Tsugite only checks before sending and after receiving.",
          "It does not check whether the additional authentication is correct or whether credentials are valid (that is the role of the external decision and the platform).",
          "Satisfying it is not approval (approval: false).",
          "It does not include an actual platform (which IdP). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not recognize every secret. Detecting token and key shapes is a net that looks only at typical forms; the real protection is the pinned reference lists. Detection of direct identifiers also covers only the typical forms of email addresses and phone numbers.",
          "It is not a template for how to use a specific IdP. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/identity-ext/v1. Input: pass a StepUpAuthRequest to submit(request) on createIdentityExtClient({ cfg, port }); cfg requires the six pinned lists. Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries step_up_state, satisfied and authorization in result. Every result has approval: false. User provides: a port (submit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE for the challenge, NONE for the verification.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-05",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Service-to-service authentication (carries workload authentication) — Authentication and Authorization Extension Tsugite",
        "summary": "The Authentication and Authorization Extension Tsugite (継手, tsugite), for A-05, checks workload (service) authentication requests against the pinned workloads and credential references, carries them to the platform, and matches the returned receipt against the contract. It does not carry the bytes of secrets or tokens; it carries only references. It derives no authority from successful authentication. If any one check fails, it does not send and does not accept (fail-closed).",
        "problem": "When authentication between services is carried to the platform, this Tsugite prevents issuing requests with workloads or credentials that are not pinned, carrying credential bytes or token-shaped values as they are, and mistaking successful authentication for authority.",
        "use_when": [
          "You want to carry authentication requests between services (workloads) to the platform only within the pinned workloads (workload_refs) and credential references (credential_refs).",
          "You want to handle credentials only as references, not as bytes.",
          "You want to match the result by request_id, request_digest and references before accepting it. The DONE result carries auth_state and authority.",
          "Even when a platform failure leaves the result unknown, you want to avoid sending the same request_id twice and resolve it with lookup."
        ],
        "do_not_use_when": [
          "You want the Workload Identity or mTLS mechanism itself. This Tsugite carries workload authentication requests by reference and does not include the function of that mechanism.",
          "You want to use a workload's successful authentication as authority. It derives no authority, and a receipt that claims to have derived workload authority is not accepted.",
          "You want to confirm that a credential is valid. It does not check the validity of credentials (that is the role of the external decision and the platform).",
          "You want to carry the values of credentials or tokens themselves. The bytes of secrets and tokens are not carried; only references are.",
          "You want an authentication platform you can use right away. No actual platform is included, and it is undecided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for A-05 alone. Under the contract, this Tsugite serves A-02, A-04, A-05, A-06 and A-07 as one part, and the tests and inspection are shared. A-01 is served by the token-verify Tsugite, and A-03 and A-08 by the session Tsugite."
        ],
        "what_it_checks": [
          "It checks workloads. It does not send a request for a workload that is not pinned. An empty list allows nothing.",
          "It checks credential references. It does not send a request for a credential that is not pinned. If a credential reference or assertion_ref has the shape of bytes or a token, it is not sent even if it is pinned.",
          "It checks the form of references. It does not send a reference containing a path or .. (path traversal), or extra fields.",
          "A receipt that claims to have derived workload authority is not accepted.",
          "If the receipt's request_id, request_digest or references do not match the request, the receipt is not accepted. A receipt with a spoofed request_digest is not accepted either.",
          "A receipt that increases the external effect, or whose effect is UNKNOWN, is not accepted. If a receipt that cannot be accepted arrives, the client stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not include the Workload Identity or mTLS mechanism itself. It only checks workload authentication requests by reference and carries them.",
          "Successful authentication is not authority. It derives no permission or authority from identity or credential state.",
          "It does not check the validity of credentials (that is the role of the external decision and the platform). Successful authentication is not approval either (approval: false).",
          "It does not include an actual platform. The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not recognize every secret. Detecting token and key shapes is a net that looks only at typical forms; the real protection is the pinned reference lists.",
          "It is not a template for how to use a specific IdP. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/identity-ext/v1. Input: pass a WorkloadAuthRequest to submit(request) on createIdentityExtClient({ cfg, port }); cfg requires the six pinned lists (workload_refs and the others). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries auth_state and authority in result. Every result has approval: false. User provides: a port to the platform (submit, and optionally lookup). Side effect: NONE or EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-06",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "API key issuance and rotation (carries references; does not let keys be disclosed) — Authentication and Authorization Extension Tsugite",
        "summary": "The Authentication and Authorization Extension Tsugite (継手, tsugite), for A-06, checks requests to issue (ISSUE), rotate (ROTATE) and revoke (REVOKE) API keys against the pinned keys and secret-location references, carries them to the platform, and matches the returned receipt against the contract. It does not carry key bytes, and it does not accept a receipt that discloses key material. If any one check fails, it does not send and does not accept (fail-closed).",
        "problem": "When API keys for external integrations are issued, rotated and revoked, this Tsugite prevents handling keys or locations that are not pinned, carrying key bytes as they are, accepting key material returned by the platform, and mistaking a key's state for authority.",
        "use_when": [
          "You want to carry requests to issue, rotate and revoke API keys to the platform only within the pinned keys (key_refs) and key locations (secret_refs).",
          "You want to hand over a new key only by a reference to its location (secret_ref), not by its material. secret_ref is required only for ISSUE and ROTATE, and the states are ISSUE to ISSUED_REF, ROTATE to ROTATED_REF, and REVOKE to REVOKED, or FAILED.",
          "You want to match the result by request_id, request_digest and references before accepting it. The DONE result carries key_state and key_material_disclosed.",
          "Even when a platform failure leaves the result unknown, you want to avoid sending the same request_id twice and resolve it with lookup."
        ],
        "do_not_use_when": [
          "You want to receive key material. A receipt that discloses key material is not accepted.",
          "You want key storage itself. This Tsugite carries issue, rotate and revoke requests to the platform by reference and does not include key storage (which key store is undecided).",
          "You want to use a valid key as permission or authority. Even a valid credential is not permission or authority.",
          "You want to confirm that a credential is valid. It does not check the validity of credentials (that is the role of the external decision and the platform).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for A-06 alone. Under the contract, this Tsugite serves A-02, A-04, A-05, A-06 and A-07 as one part, and the tests and inspection are shared. A-01 is served by the token-verify Tsugite, and A-03 and A-08 by the session Tsugite."
        ],
        "what_it_checks": [
          "It checks keys and locations. It does not send a request for a key that is not pinned or a secret location that is not pinned. An empty list allows nothing.",
          "It checks secret_ref. A key-shaped secret_ref is not sent even if it is pinned. A REVOKE request with a secret_ref attached is not sent either.",
          "A receipt that discloses key material is not accepted.",
          "It checks the form of references. It does not send a reference containing a path or .. (path traversal), or extra fields.",
          "If the receipt's request_id, request_digest or references do not match the request, the receipt is not accepted. A receipt with a spoofed request_digest is not accepted either.",
          "A receipt that increases the external effect, or whose effect is UNKNOWN, is not accepted. If a receipt that cannot be accepted arrives, the client stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not carry or receive key material. It handles only references to keys and their locations.",
          "It derives no permission or authority from a key's state. It does not check the validity of credentials either (that is the role of the external decision and the platform).",
          "Successful issuance, rotation or revocation is not approval (approval: false).",
          "It does not include an actual platform (which key store). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not recognize every secret. Detecting token and key shapes is a net that looks only at typical forms; the real protection is the pinned reference lists.",
          "It is not a template for how to use a specific key storage service. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/identity-ext/v1. Input: pass an ApiKeyLifecycleRequest to submit(request) on createIdentityExtClient({ cfg, port }); cfg requires the six pinned lists. Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries key_state and key_material_disclosed in result. Every result has approval: false. User provides: a port to the platform (submit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-07",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Delegation (on-behalf-of) reference — Authentication and Authorization Extension Tsugite",
        "summary": "The Authentication and Authorization Extension Tsugite (継手, tsugite), for A-07, checks only delegation references backed by evidence received from a person or organization, carries them to the platform, and matches the returned receipt against the contract. ERABAZU does not create delegation authority. Delegation authority comes only from the evidence of an external person or organization. If any one check fails, it does not send and does not accept (fail-closed).",
        "problem": "When an \"on behalf of the subject\" delegation is carried to the platform, this Tsugite prevents carrying a delegation without evidence from a person or organization, making ERABAZU itself the origin of a delegation, and accepting a receipt that claims to have created delegation authority.",
        "use_when": [
          "You want to receive delegation evidence issued by a person or organization with recordAuthorityEvidence(evidence_ref, origin) (origin is only HUMAN or ORGANIZATION), and carry to the platform only delegation references backed by that evidence.",
          "You want to handle the parties to a delegation only by pseudonymous references, not by email addresses or phone numbers.",
          "You want the handover of a delegation to cause no external effect. The side effect of a delegation reference is NONE.",
          "You want to match the result by request_id, request_digest and references before accepting it. The DONE result carries delegation_state and authority_origination.",
          "Even when a platform failure leaves the result unknown, you want to avoid sending the same request_id twice and resolve it with lookup."
        ],
        "do_not_use_when": [
          "You want ERABAZU to create delegation authority, or to hand over authority itself. This Tsugite carries delegation references, and ERABAZU does not create delegation authority.",
          "You want to carry a delegation without evidence from a person or organization. Such a request is not sent (REFUSED).",
          "You want to confirm that a delegation is genuine. It does not check the content of the delegation evidence; it only checks whether the reference was received from a person or organization.",
          "You want a platform you can use right away. No actual platform is included, and it is undecided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for A-07 alone. Under the contract, this Tsugite serves A-02, A-04, A-05, A-06 and A-07 as one part, and the tests and inspection are shared. A-01 is served by the token-verify Tsugite, and A-03 and A-08 by the session Tsugite."
        ],
        "what_it_checks": [
          "It checks delegation evidence. It does not send a delegation without evidence, a delegation based on evidence not received through recordAuthorityEvidence, or a delegation whose origin is ERABAZU.",
          "A receipt that claims to have created delegation authority is not accepted.",
          "It checks party references and the form of references. It does not send a reference in the form of an email address or phone number, a reference containing a path or .. (path traversal), or extra fields.",
          "If the receipt's request_id, request_digest or references do not match the request, the receipt is not accepted. A receipt with a spoofed request_digest is not accepted either.",
          "A receipt that increases the external effect, or whose effect is UNKNOWN, is not accepted. If a receipt that cannot be accepted arrives, the client stops (HALTED).",
          "It does not send the same request_id twice. A platform failure is treated as UNKNOWN, and resending is resolved with lookup."
        ],
        "what_it_does_not_do": [
          "ERABAZU does not create delegation authority. Delegation authority comes only from the evidence of an external person or organization. This Tsugite carries delegation references and does not include any function for handing over authority.",
          "It does not check the content of the delegation evidence (whether the delegation is genuine). It only checks whether the reference was received from a person or organization.",
          "Carrying a delegation is not approval (approval: false).",
          "It does not include an actual platform. The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not recognize every direct identifier. Detection covers only the typical forms of email addresses and phone numbers.",
          "It is not a template for how to use a specific IdP. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/identity-ext/v1. Input: receive evidence from a person or organization with recordAuthorityEvidence(evidence_ref, origin) on createIdentityExtClient({ cfg, port }), then pass a DelegationRefRequest to submit(request). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries delegation_state and authority_origination in result. Every result has approval: false. User provides: a port to the platform (submit, and optionally lookup). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-08",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Revocation propagation — Session and Revocation Propagation Tsugite",
        "summary": "The Session and Revocation Propagation Tsugite (継手, tsugite) sends a session revocation (REVOKE) to the store, matches the receipt, and keeps the revocation \"incomplete\" until reach receipts with evidence have arrived from every pinned propagation target. While it is incomplete, the session is still treated as unusable (fail-closed).",
        "problem": "It prevents the \"logged out, yet the session remains\" accident. It prevents treating a revocation accepted in one place as having reached the whole system, and prevents trusting reach notices without evidence or from targets that are not pinned. It is a check that confirms, in a form that can be matched, whether a revocation has reached the whole system.",
        "use_when": [
          "You want to confirm, by reach receipts, whether a logout or revocation has reached every propagation target you set (propagation_targets).",
          "You want to know which propagation targets it has not reached yet. When incomplete, the result comes with the targets not reached (missing_targets).",
          "You want revocations to keep being sent even while the client is stopped because of an inconsistent receipt. This Tsugite also accepts revocation of sessions it does not know.",
          "You want a revoked session never to be resumed afterwards."
        ],
        "do_not_use_when": [
          "You want it to deliver the revocation to the propagation targets itself. It only checks reach by the reach receipts that the port (propagation) returns, and does not include the propagation targets' implementation.",
          "You want propagation targets or a session store you can use right away. The actual session store and propagation targets (each service's revocation intake) are undecided.",
          "You want to revoke or verify the token itself. Token verification is the role of A-01 (token-verify-tsugite); this Tsugite handles only session operations and revocation propagation.",
          "You want to treat a revocation having reached everywhere as identity verification, authority or approval. Every result is authority: NOT_DERIVED and approval: false.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks for missing propagation targets. If even one pinned propagation target has no reach receipt, it does not treat the revocation as complete and returns REVOKED_PROPAGATION_INCOMPLETE.",
          "It checks the evidence of reach. A reach notice without evidence (evidence_ref) is not accepted as having reached.",
          "It checks the pinning of propagation targets. A reach notice from a target that is not pinned is not accepted as having reached.",
          "It matches the revocation receipt. A REVOKE receipt must carry a propagation reference, and if the state contradicts the operation (REVOKE must give REVOKED), the receipt is for another session, or request_digest is spoofed, it is not accepted.",
          "It rejects anything remaining after revocation. A revoked or terminated session is not resumed, and if the store returns RESUMED after a revocation, it does not accept it and stops.",
          "Whether the revocation is complete or incomplete, the session is treated as unusable. On a store failure (UNKNOWN), it is also treated as unusable until resolved."
        ],
        "what_it_does_not_do": [
          "It has no mechanism of its own for delivering revocations to propagation targets. The shape of the reach receipt (target_ref, session_ref, revoked, evidence_ref) is not in the contract; it is defined on the Tsugite side.",
          "It does not include an actual session store or propagation targets (each service's revocation intake). The port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It does not verify or revoke tokens. Token verification is the role of A-01 (token-verify-tsugite).",
          "A revocation having reached everywhere is neither identity verification nor authority (authority: NOT_DERIVED, approval: false).",
          "A-08 is one part with A-03 (session management) under the contract. Each ID's row (A-03 and A-08) points to this part.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a session store."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1. Input: pass a REVOKE SessionOpRequest to apply(request) on createSessionClient({ cfg, port }); the propagation targets are pinned in cfg propagation_targets. Returns: REVOKED_EVERYWHERE or REVOKED_PROPAGATION_INCOMPLETE (with missing_targets), among others. Every result has authority: NOT_DERIVED and approval: false. User provides: a store port (apply) and a port that returns the list of reach receipts (propagation). Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-09",
        "category": "A/認証・認可",
        "kind": "Tsugite",
        "title": "Organization domain control lookup — Organization Domain Control Lookup Tsugite",
        "summary": "The Organization Domain Control Lookup Tsugite (継手, tsugite) has the port look up a marker placed on an organization's domain, either in DNS TXT (`_kokoroamu.<domain>`) or at a fixed HTTPS location (`/.well-known/kokoroamu-org.json`), matches the returned receipt (OrgDomainControlReceipt) against the contract, and carries the answer exactly as received. It does not treat a successful lookup or a found marker as proof of domain control (fail-closed).",
        "problem": "When someone says \"the domain has been checked\", it prevents losing sight of where the lookup read and which answer was trusted. Names and URLs are built from the domain alone; redirects, TLS, DNSSEC and sizes are checked; and the answer is carried exactly as received, together with its digests. It does not mistake a lookup for proof of domain control, and it does not reuse an answer for a different purpose.",
        "use_when": [
          "You want to look up a marker placed on an organization's domain, in DNS TXT (`_kokoroamu.<domain>`, method DNS_TXT) or at a fixed HTTPS location (`/.well-known/kokoroamu-org.json`, method HTTPS_WELL_KNOWN), and pass the answer on to the side that reads it later.",
          "You want callers to pass only the domain, and not let them decide the DNS name, URL, port number or path.",
          "You want to use one domain in separate lookups for separate purposes (the current table of purposes is `seat8`). The purpose is part of both the request and the answer, and an answer is not reused for a different purpose (reusable_for_other_purpose false).",
          "You want to keep the TXT records in the order received and the HTTPS body as bytes (base64), each with its digest."
        ],
        "do_not_use_when": [
          "You want to read the marker, match the purpose and key ID, and decide the validity period. That is the role of the A-10 part (org-domain-claim-primitive) and of rules and people; this Tsugite only performs the lookup.",
          "You want to use the lookup result as proof of domain control as it stands. An answer from this Tsugite does not recognize domain control (ownership_decision NOT_DERIVED).",
          "You want to look up an arbitrary URL, port number or path. The request has no such fields and does not accept them.",
          "You want a lookup mechanism you can use right away. No actual DNS or HTTPS lookup has been made (0 times); the port implementation, the resolver, the list of trusted CAs and the setting values will be decided at a separate gate when connecting.",
          "You want to keep lookup records for a long time. Records exist only inside this Tsugite and are lost on restart.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the settings and the purpose. It does not send the request (REFUSED) when the request's contract_pin differs from the setting contract_schema_digest, when there are no settings, when the purpose is not in allowed_purposes, or when the resolver is not in resolver_profile_refs. An empty list allows nothing.",
          "It checks that the name and URL in the receipt are the same as those built from the domain. It does not accept a receipt whose purpose, domain or key ID differs from the request.",
          "It checks the content of the answer. It does not accept an answer whose TXT order, record count or digest, or whose body size, digest or content type (JSON or text, charset=utf-8 only) does not match. Strings in the receipt and the request are accepted only when they are valid Unicode.",
          "It checks redirects and TLS. It does not accept an answer that followed a redirect to a different host, to http, or beyond the limit (a redirect that was not followed is REDIRECT_REFUSED). It also does not accept a FOUND or NOT_FOUND answer that failed a TLS check, an answer containing a DNSSEC BOGUS, or a partial answer from a TIMEOUT.",
          "It checks the connected address (connected_address). It accepts only an address that is global according to the narrowest row containing it in the IANA special-purpose address registry. An answer that connected inside a NAT64 prefix is accepted only when the embedded IPv4 address extracted according to RFC 6052 is also global.",
          "It matches the markers of not deciding, and the effect. It does not accept a receipt that does not keep claim_evaluation NOT_PERFORMED, ownership_decision NOT_DERIVED and side effect NONE, and it stops after a receipt it cannot match (HALTED after RECEIPT_REJECTED).",
          "It does not send different content under the same request_id. A port failure or a timeout (limits.timeout_ms plus a 1-second grace period) is treated as UNKNOWN and resolved by sending the same request again (first through port.lookup, if present)."
        ],
        "what_it_does_not_do": [
          "It does not read the marker (`v=kokoroamu-org1`), match the purpose and key ID, decide the validity period, or recognize domain control. A successful lookup or a found marker is not proof of domain control.",
          "It does not check whether all resolved addresses were global, whether cookies or credentials were withheld, or whether the name and URL were actually built from the domain (the role of the port). What this Tsugite checks extends only to the connected_address in the receipt being global.",
          "DNSSEC SECURE or INSECURE, the results of the three TLS checks, and the TTL are what the port reports; this Tsugite checks only their form and combinations.",
          "The table of global ranges is a copy made by reading the IANA registry as a web page, and it has not been compared with the raw registry bytes. It needs to be compared again with the raw CSV files before publication.",
          "It does not know the local NAT64 prefix. For an answer from a port that does not return nat64, it cannot check whether an IPv4 address embedded in the local prefix is global.",
          "It writes nothing (side effect NONE). Having carried or looked something up is not approval (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/org-domain-control/v1 (OrgDomainControlRequest / OrgDomainControlReceipt). Input: pass an OrgDomainControlRequest to submit(request) on createOrgDomainControlClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: a lookup port (submit, and optionally lookup) and cfg (contract_schema_digest, allowed_purposes and resolver_profile_refs). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-10",
        "category": "A/認証・認可",
        "kind": "Primitive",
        "title": "Organization domain mark reading — Organization domain mark reading and matching part (Primitive)",
        "summary": "The Organization domain mark reading and matching part (Primitive) is a standalone part that reads the mark a company (organization) has placed on its own domain (a `_kokoroamu.<domain>` line in DNS TXT, or the JSON document at `/.well-known/kokoroamu-org.json` over HTTPS) from the raw answer received by a lookup, matches it by exact match of purpose and key ID, and creates a record with an expiry (claim). Lines it cannot read and lines for other uses are not discarded; they are returned as facts.",
        "problem": "Variations in how a mark is written, and marks that have expired, are not visible from the outside. This part does not read marks with unknown fields, duplicate fields or an unknown version, and passes only purposes that are in the caller's allowed_purposes. It prevents the confirmation of one domain from being reused for another purpose and widening authority, and it decides the expiry from the observation time and a number of days.",
        "use_when": [
          "You want to read the list of marks (purpose and key ID) from a DNS TXT answer (txt_records) or from a well-known body (body_base64 and content_type) (parseMarks).",
          "You want to confirm, by exact string match, whether there is a mark for a given purpose and a given key ID, and create a record (claim) of domain, method, purpose, key_id, observed_at and expires_at (checkClaim).",
          "You want lines it cannot read and lines for other uses (SPF and so on) kept with a reason (the reason in ignored) instead of being silently discarded.",
          "You want to confirm whether a record is within its validity period at a time you pass (at) (isClaimCurrent; when outside it, reason is NOT_YET_OBSERVED or EXPIRED).",
          "You want to create attestation attributes from a record (exactly one domain:<domain> and one purpose:<purpose>, in this order) (claimToAttestationAttributes).",
          "You want to call it locally as deterministic processing that has no clock, no random numbers and no outside connection. The part does not hold a table of purposes; the caller passes it as allowed_purposes."
        ],
        "do_not_use_when": [
          "You want to confirm that the mark came from that domain (the DNS name, DNSSEC, the HTTPS certificate, redirects). This part reads the answer it is given as belonging to that domain. The lookup and its checks are the role of the lookup part (the A-09 proposal) and of the caller.",
          "You want to confirm the identity of the domain's owner (that the individual or organization exists). What this part reads is only the mark showing control of the domain.",
          "You want a domain containing uppercase letters, a trailing dot or leading or trailing white space, or an IP address, to be corrected and accepted. It does not correct the name; it refuses it with INVALID_DOMAIN.",
          "You want to pass an internationalized domain in Unicode form. It accepts only the xn-- form and does not check the content of the punycode.",
          "You want a validity period longer than 90 days. valid_days is only an integer from 1 to 90; outside that range it stops with INVALID_VALID_DAYS.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It reads a DNS TXT line as a mark only when the first field is exactly the fixed version name (`v=kokoroamu-org1`) and the rest is exactly one purpose=<purpose> and one key=<key ID>. It does not read lines with unknown fields, duplicate fields, empty fields, missing fields or an unknown version; it returns them in ignored with a reason (UNKNOWN_FIELD, DUPLICATE_FIELD, MISSING_FIELD, UNKNOWN_VERSION, MALFORMED_MARK and so on).",
          "It puts a line longer than 4096 characters into ignored as TOO_LONG, a line that is not UTF-8 (in the { undecodable_base64 } form) as NOT_UTF8 without decoding it, and a line that is not a string as NOT_TEXT. It stops with INVALID_RECORDS or TOO_MANY_RECORDS when txt_records is not an array or has more than 64 items.",
          "For a well-known body, it stops with UNSUPPORTED_CONTENT_TYPE when content_type is not application/json or text/plain (optionally with ; charset=utf-8), and with INVALID_BODY when body_base64 is not canonical base64 or is larger than 64 KiB when decoded. A body that is not UTF-8, is not JSON, has the wrong document shape or has more than 1000 entries is returned as the fact NOT_UTF8, NOT_JSON, MALFORMED_DOCUMENT or TOO_MANY_ENTRIES.",
          "When purpose is not in allowed_purposes, the result is matched: false and reasons records PURPOSE_NOT_ALLOWED. Even with the same key ID, a mark for another purpose does not pass; it records KEY_UNDER_OTHER_PURPOSE.",
          "It compares purpose and key ID by exact string match. Key IDs are case-sensitive, and it does no Unicode normalization or case folding.",
          "When domain, purpose, key_id, allowed_purposes, observed_at, valid_days or marks has the wrong form, it stops with INVALID_DOMAIN, INVALID_PURPOSE, INVALID_KEY_ID, INVALID_ALLOWED_PURPOSES, INVALID_OBSERVED_AT, INVALID_VALID_DAYS or INVALID_MARKS. A domain is up to 242 characters, a time is in UTC Z form from the year 2000 to 9998, and marks is up to 2000 items.",
          "When the claim passed to isClaimCurrent or claimToAttestationAttributes has the wrong form (extra keys, an expiry that is not after observed_at, more than 90 days), it stops with INVALID_CLAIM. When at has the wrong form, it is INVALID_AT. Only claimToAttestationAttributes throws OrgDomainClaimError.",
          "It stops with INVALID_INPUT when the input is not an object, and with UNKNOWN_SOURCE when source is neither DNS_TXT nor HTTPS_WELL_KNOWN. When it stops, it returns { ok: false, errors: [{ code }] }."
        ],
        "what_it_does_not_do": [
          "matched: true is only the fact that the answer it was given contained a mark for this purpose and this key ID. It does not show the identity of the domain's owner, that the mark came from that domain, or that the attestation may be signed. Whether to sign is decided by the caller.",
          "It does not perform DNS or HTTPS lookups. It has no outside boundary, no state and no random numbers, and it does not read the clock (all times are input).",
          "It does not hold a table of purposes. Which purposes to allow (allowed_purposes) is passed by the caller.",
          "For duplicate keys in JSON it uses the last value, as JSON.parse does; it does not detect duplicates and refuse them.",
          "It does not decode { undecodable_base64 } values. It does not check whether they are canonical base64 or really not UTF-8 (that is checked on the receiving side), and it does not read lines in that form as marks.",
          "Creating a record (claim) is not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for DNS or HTTPS lookups."
        ],
        "integration": "Call parseMarks, checkClaim and isClaimCurrent (they do not throw) and claimToAttestationAttributes (it throws) in src/org-domain-claim.js. parseMarks takes txt_records, or body_base64 and content_type, and returns marks and ignored. checkClaim takes domain, purpose, key_id, allowed_purposes, marks, observed_at and valid_days, and returns matched, reasons and claim. The shape of the answers it reads follows the contract org-domain-control/v1. There are no side effects, no state and no credentials, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "A-11",
        "category": "A/認証・認可",
        "kind": "Component",
        "title": "Organization domain control check — Organization domain control check part (Component)",
        "summary": "The Organization domain control check part (Component) is a part that bundles the lookup Tsugite (A-09) and the mark-matching part (Primitive) (A-10) and performs check({ domain, purpose, key_id }) in one call. It first looks up DNS TXT (`_kokoroamu.<domain>`); if there is a mark whose purpose and key ID match exactly and it is within its validity period, the result is MATCHED (dns_txt). Otherwise it looks up the HTTPS well-known location, and if that matches, the result is MATCHED (https_well_known). When both answer and there is no match, the result is NOT_FOUND; when it cannot decide, UNAVAILABLE (fail-closed). MATCHED is not a judgment of domain control.",
        "problem": "It is a part that fixes, behind the organization verification screen, the order of lookups and the conditions for refusing. It prevents passing on a mark for another purpose or another key, confusing an answer for a different domain, purpose, key or method, deciding \"not there\" when it cannot decide, and making its own judgment about the validity period.",
        "use_when": [
          "You want to check, in one call, whether an organization's domain has a mark for a set purpose and key ID (16 lowercase hex digits), looking up DNS first and then HTTPS.",
          "You want to limit the purposes that may be looked up to a table in the options (allowed_purposes, 1 to 64 entries; currently `seat8`), so that a purpose not in the table is not even looked up.",
          "You want to receive the result as one of three states, MATCHED, NOT_FOUND and UNAVAILABLE, and handle \"both answered and there was no match\" separately from \"could not decide (can be retried later)\".",
          "You want to leave the judgment of a mark's validity period to isClaimCurrent of A-10, using the time read after the lookup's receipt was received (the record's validity in days is valid_days, 1 to 90).",
          "On MATCHED, you want to attach the request_id of the receipt of the deciding lookup (receipt_ref) and the time read before the lookup was sent (observed_at) to a later record."
        ],
        "do_not_use_when": [
          "You want to use MATCHED as a judgment of control over the domain or as grounds that signing is allowed. MATCHED is only the fact that \"a mark for this purpose and this key ID was in this domain's DNS or well-known location at the time of the lookup\".",
          "You want this part to also enforce a limit on the number of lookups for the same device or domain. That limit is the job of the caller's rules and of a person.",
          "You connect a lookup port that does not answer directly. This part does not cut off waiting, so check does not finish (the real A-09 client returns UNKNOWN after limits.timeout_ms plus 1 second).",
          "You need the receipt that receipt_ref points to to be retrievable after a restart. The A-09 client's record is only inside the process and is lost on restart; you rely on the caller's record.",
          "You want it to also build attestation attributes. claimToAttestationAttributes of A-10 is not used. Building the attributes is the caller's job.",
          "You need a part checked against real DNS and HTTPS. It has made 0 real lookups.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, it stops with INVALID_OPTIONS. It refuses with INVALID_INPUT when the input to check is not an object or cannot be read, with INVALID_DOMAIN when domain is not a DomainName of the contract (up to 242 characters), with PURPOSE_NOT_ALLOWED when purpose is not in the table, and with INVALID_KEY_ID when key_id is not 16 lowercase hex digits. When it refuses, it makes 0 lookups, does not read the clock, and state is null.",
          "When the clock throws, does not return a number, or returns a time outside the years 2000 to 9998, it stops there with CLOCK_FAILED and returns UNAVAILABLE (no further lookups).",
          "When the answer from A-09 is REFUSED, it reports LOOKUP_REFUSED; when UNKNOWN, LOOKUP_UNKNOWN; when RECEIPT_REJECTED or HALTED, LOOKUP_HALTED; when anything other than DONE (including ALREADY_DONE), LOOKUP_NOT_DONE; and when a lower part throws an exception or rejects, PORT_FAILED; and it does not return MATCHED.",
          "Even with DONE, when the result does not echo the requested domain, purpose, key ID and method, approval is not false, or result is missing, it does not use the result (RESULT_MISMATCH).",
          "When the lookup could not answer (TIMEOUT, FAILED, TLS_FAILED, REDIRECT_REFUSED, an unknown state), it reports LOOKUP_FAILED. FAILED means \"could not answer\" whatever the reason (PRIVATE_ADDRESS, DNSSEC_BOGUS, CONTENT_TYPE_REFUSED and others).",
          "When parseMarks of A-10 cannot read the marks, it reports MARKS_UNREADABLE; when checkClaim does not return a boolean matched or returns a claim different from what was requested, CLAIM_UNCHECKED; and when isClaimCurrent does not return current: true (NOT_YET_OBSERVED, EXPIRED and others), CLAIM_NOT_CURRENT; and it does not return MATCHED.",
          "It copies the options and the purpose table when it is created, and also copies the table it passes to A-10. Even if options or the table are rewritten later, the next check does not widen."
        ],
        "what_it_does_not_do": [
          "The Organization domain control check part is not a Tsugite. It has no Request or Receipt of its own; the external lookups are made by the bundled A-09 Tsugite under its own contract (org-domain-control/v1). This part itself does not touch the outside, and the lookups only read (side effect NONE).",
          "The checks of the lookup (receipt matching, redirects, TLS, DNSSEC, the connected address, content type) stay on the A-09 side, and reading, matching and the validity period of marks stay on the A-10 side. That a mark came from that domain is the job of A-09 and the port; this part trusts DONE from A-09.",
          "MATCHED is not a judgment of domain control, and it is neither authority nor approval (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "It does not compare the receipt's queried_at (as declared by the port) against the clock. This part also does not cut off waiting for a lookup.",
          "It has never made a lookup against real DNS or HTTPS. The tests and fuzzing use stand-ins, and the wiring was checked with the code of the real A-09 and A-10 against the A-09 test stand-in port.",
          "Where the purpose table is kept, and names for purposes other than the current one, are not decided in this part (left open in the contract).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular DNS service or HTTP client."
        ],
        "integration": "Create it with createOrgDomainCheck(options) in src/org-domain-check.js; it returns { ok, errors, instance }. options are allowed_purposes, contract_schema_digest, resolver_profile_ref, limits, valid_days, request_prefix, clock and ports. check returns { ok, errors, state, method, observed_at, receipt_ref, approval: false, … }. Ports: domainControl.submit (the client of the A-09 Tsugite) and parseMarks, checkClaim and isClaimCurrent of A-10. No credentials are needed.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-01",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "Signing (KMS/HSM) — KMS Signing Tsugite",
        "summary": "The KMS Signing Tsugite (継手, tsugite) fronts a backstage signing API (Cloud KMS/HSM). It accepts only adopted schemas and contexts and RFC 8785-canonicalized payloads, checks the sequence and previous_hash chain before requesting a signature, and verifies the returned signature with a pinned public key (PEM) before returning it. On any mismatch it rejects without calling KMS (fail-closed).",
        "problem": "When signing artifacts or records, it prevents signing arbitrary byte strings, signing formats or contexts that have not been adopted, and signing while the signing order (sequence) or the link to the previous one (previous_hash) is broken. It is a check that turns the signing API into a fail-closed part that does not accept arbitrary input.",
        "use_when": [
          "You want to sign artifacts and records only for adopted schemas and contexts and canonicalized payloads.",
          "You want to arrange signatures in an append-only chain per context (monotonically increasing sequence and a previous_hash chain).",
          "You want to verify a returned signature on the spot with a pinned PEM, without fetching the public key each time, before using it.",
          "You want to use Cloud KMS AsymmetricSign (EC_SIGN_P256_SHA256) with authentication that keeps no JSON key (attached SA / ADC) and least privilege per key."
        ],
        "do_not_use_when": [
          "You want to sign arbitrary byte strings or files as they are. canonical_payload is limited to strings canonicalized with RFC 8785, and arbitrary payloads are rejected.",
          "You also want this Tsugite to do the canonicalization. RFC 8785 canonicalization is done by the caller.",
          "You want a part tested while connected to an actual Cloud KMS and Firestore. This part's tests use a local P-256 key and an in-memory store; the connection to production Cloud KMS and Firestore has not been checked.",
          "You want to treat a signature as approval or a decision. This Tsugite only checks and passes the signature on; it grants no authority.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by fusion Kernels."
        ],
        "what_it_checks": [
          "It checks the schema and context. Schemas and contexts that have not been adopted, and a wrong context (wrong-context), are rejected with 422 without calling KMS.",
          "It checks the sequence. A sequence that rolls back, is duplicated or is not an integer is rejected without calling KMS.",
          "It checks previous_hash. Anything that does not link to the SHA-256 of the previous payload (or GENESIS), and violations of the genesis rules (including when the genesis for approval is not defined), are rejected.",
          "It checks the payload. Requests without canonical_payload, and arbitrary payloads, are rejected.",
          "It matches the contents of the payload against the request. It signs only when the payload's context and schema_version are the same as the request's, and does not sign a payload that differs.",
          "A request naming an unknown key is rejected with 400. The key is erabazu_publisher or approval_checkpoint.",
          "It verifies the signature. If the signature returned by KMS cannot be verified with the pinned public key (PEM), it returns 500 and no signature."
        ],
        "what_it_does_not_do": [
          "This part's tests (unit tests and fuzzing) did not connect to production Cloud KMS or Firestore; they used a local P-256 key and an in-memory store. Separately, there is a record from 2026-08-15 of signing a synthetic context (erabazu.synthetic-test) with the production ERABAZU HSM key and verifying it with the pinned PEM.",
          "It does not canonicalize canonical_payload (RFC 8785). The caller does.",
          "It does not sign arbitrary input. It is limited to the context, the schema and the canonicalized payload.",
          "The next sequence and previous_hash returned by GET /latest are only a hint. Whether a signature is made is decided by the checks of POST /sign.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a signing API."
        ],
        "integration": "Input: send { key, context, schema_version, sequence, previous_hash, canonical_payload } to POST /sign. The entry point is src/server.js (handleSign), and the configuration is app_kms_config.json. Returns: one of 200 (with signature_der_base64 (an ECDSA DER signature), payload_sha256, verified_with_pinned_pem: true and other fields), 400 (unknown key), 422 (fail-closed, with reasons) or 500 (the signature could not be verified with the pinned key; no signature is returned). User provides: a signer port (Cloud KMS in production) and a store port (getLatest and commit; Firestore in production).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-02",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Signature verification (pinned) — Signature verification (pinned) part (Primitive)",
        "summary": "The Signature verification (pinned) part (Primitive) is a standalone part that verifies, locally, a signature over bytes with a public key that the caller has fixed (pinned). The only algs are Ed25519 and ES256, and keys are taken only from pinned (key_id → { alg, public_key_jwk }); a request cannot bring in a key. verify does not throw an exception and returns { ok, errors }, and a value it cannot accept is stopped with a fixed code (fail-closed).",
        "problem": "For the problem that nobody checks whether what has been received is \"genuine\", it verifies origin and integrity locally. While doing so, it stops in advance: verifying with a key brought in by the request, confusing one alg for another, the configuration mistake of putting a private-key JWK into pinned, and non-canonical encodings that the Node.js key import would otherwise accept.",
        "use_when": [
          "You want to verify the signature on bytes you received using only public keys that the caller has decided on and fixed (pinned).",
          "You want to verify a signature whose alg is Ed25519 (R||S per RFC 8032) or ES256 (ECDSA on P-256 with SHA-256; the signature is the 64-byte IEEE P1363 r||s).",
          "You need verification that does not throw. verify returns { ok, errors }, does not throw an exception, and does not modify its input.",
          "You want to verify the signature of a JSON value. Turn it into canonical bytes with the I-08 part first, then pass them to this part.",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no outside boundary and no state. It has no dependency packages."
        ],
        "do_not_use_when": [
          "You want to confirm who the signer is (identity). A correct signature shows only that the holder of the pinned key signed these bytes.",
          "You want this part to decide which keys to pin, to handle key rotation and revocation, or to assign key_id values. These are decided outside this part (by the caller and by trust management).",
          "You want to use the signature bytes as a unique identifier. ES256 signatures are malleable ((r, n - s) is also valid), and low-S is not enforced.",
          "You want to verify algs other than Ed25519 and ES256 (EdDSA, ES384, RS256, none and so on), ES256 signatures in DER form, or prehashed values.",
          "You want to handle the whole of JWS (the header, assembling the payload, the base64url separators). This part handles only the verification of the signature bytes.",
          "You want to align the strictness of Ed25519 (small-order points, non-canonical points, differences such as RFC 8032 versus ZIP-215) with your own rules. It follows the OpenSSL implementation and does not refuse public keys that are small-order points either.",
          "You use it on a version other than Node.js v24.21.0 (including differences in the OpenSSL version). Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the request is not a plain object it stops with NOT_A_REQUEST, and when it has fields other than alg, key_id, signature and bytes (such as public_key_jwk or seed, which would bring in a key) it stops with UNKNOWN_FIELD.",
          "When alg is neither \"Ed25519\" nor \"ES256\" (including differences in case, and none) it stops with UNSUPPORTED_ALGORITHM, and when key_id is not a string of 1 to 256 characters it stops with INVALID_KEY_ID.",
          "When signature is not canonical base64url (padding, standard base64 characters, whitespace, or leftover bits) it stops with MALFORMED_SIGNATURE, and when it does not decode to 64 bytes (including DER) it stops with INVALID_SIGNATURE_LENGTH.",
          "When bytes is not a Uint8Array (a string, another typed array, a DataView or a Proxy), it stops with NOT_BYTES.",
          "When pinned is not a plain object it stops with INVALID_PINNED_SET, and when a JWK anywhere in pinned (including non-enumerable entries) has private members, it stops with PRIVATE_KEY_REFUSED whatever the key_id.",
          "When key_id is not an own member of pinned (including prototype names such as __proto__) it stops with UNKNOWN_KEY, and when a pinned entry or JWK has the wrong shape or is a point not on the curve it stops with INVALID_PINNED_KEY.",
          "When the request's alg differs from the pinned alg it returns ALG_MISMATCH, and when the bytes, the signature or the key differ (including S + L for Ed25519) it returns SIGNATURE_INVALID.",
          "When inspecting the input itself raises an exception (a getter, a Proxy trap and so on), it returns INSPECTION_FAILED and does not throw. The input is read only once, and the key import is given a JWK that this function has rebuilt."
        ],
        "what_it_does_not_do": [
          "It does not include key distribution, deciding what to pin, rotation and revocation (trust management), or assembling JWS or manifests.",
          "It does not create signatures. Private keys are handled by Tsugite such as KMS, and this part does not accept a JWK that has private members.",
          "The elliptic-curve computation is left to Node.js crypto (OpenSSL) and has not been checked independently. The fuzz oracle also only calls the same crypto through a different way of importing the key.",
          "It was checked against only one RFC 8032 example (Example 1), and ES256 was checked with signatures from keys generated at run time instead of public known answers.",
          "A correct signature is not the signer's identity, legal acceptance, authority or approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Compatibility with the signature verification inside other parts or Tsugite has not been checked.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular signature library."
        ],
        "integration": "Call verify(request, pinned) in src/signature-verify.js (it does not throw). Input: a plain object request with only alg, key_id, signature and bytes (a Uint8Array), and a plain object pinned that maps key_id → { alg, public_key_jwk } (public JWKs only). Returns: { ok: true, errors: [] } or { ok: false, errors: [{ code }] } (code is from error_taxonomy). Keys are received through pinned and are not fetched. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are the Node.js built-in crypto and util.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-03",
        "category": "B/暗号・信頼",
        "kind": "Component",
        "title": "Encryption at rest (envelope) — Encryption at rest (envelope) part (Component)",
        "summary": "The Encryption at rest (envelope) part (Component) is a part that, to put data into envelope encryption, bundles the Tsugite that has KMS generate a DEK and unwrap a wrapped key (kms-envelope) with the Primitive that seals and opens an AES-256-GCM envelope locally (envelope-encrypt). From KMS it receives only a handle and a reference to the wrapped key; the DEK passes from withDataKey inside the port only to the local seal and open, within the same process. If the envelope's key_ref, the wrapped key's reference and the KMS key do not match, it does not send to KMS, and if the encryption context differs, it does not open (fail-closed).",
        "problem": "Behind \"it is supposed to be encrypted\", it prevents the plaintext DEK from remaining in the part's results or state, sending to KMS an envelope whose wrapped key reference or KMS key does not match, and opening an envelope with a different encryption context, a tampered envelope or an envelope with a different aad.",
        "use_when": [
          "You want to put data (plaintext, Uint8Array) into an envelope with a DEK generated by KMS (encrypt). Inject a client of the kms-envelope Tsugite as ports.kms, and the envelope-encrypt Primitive as ports.seal and ports.open.",
          "You want to use the plaintext DEK without taking it outside the KMS port. keyAccess.withDataKey(handle, fn) inside the port passes the DEK to fn, and this part does not take the DEK outside fn.",
          "You want to bind the encryption context to object_ref and context_digest (64 hexadecimal digits) so that an envelope with a different context is not opened.",
          "You want to store the envelope, the wrapped key (wrapped_key) and the digest of the encryption context together and open them with decrypt. Before opening, it checks that the envelope's key_ref, the wrapped key's reference and the KMS key match."
        ],
        "do_not_use_when": [
          "You want to use having encrypted or opened data as permission to use the data, as authority or as approval. Encryption is not access control, and every result carries approval: false and authorization: NOT_DERIVED.",
          "You also want it to store wrapped keys and envelopes or to rotate keys. Storing is the caller's role, and key rotation is the role of the B-05 Tsugite.",
          "You want to rely on the DEK being reliably erased from memory. Confining the DEK inside the port and zeroing it are the port's role and have not been checked.",
          "You want the same envelope from the same input. The Primitive generates the IV randomly, so the envelope differs every time.",
          "You handle plaintext larger than 16 MiB or aad larger than 64 KiB. The limits remain those of the Primitive.",
          "You need a part checked against a real KMS. It has never connected to a real KMS.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, a reference is invalid or a port function is missing, it stops with INVALID_OPTIONS, when the input is malformed (not bytes, a context_digest that is not 64 hexadecimal digits, the shape of wrapped_key and so on), with INVALID_INPUT, and when a getter throws an exception, with INSPECTION_FAILED.",
          "When the envelope's key_ref and the wrapped key's reference do not match, or the wrapped key's KMS key and its own kms_key_ref do not match, it stops with WRAPPED_KEY_MISMATCH without sending to KMS.",
          "When the encryption context differs, it stops with CONTEXT_MISMATCH if KMS finds it, or with KMS_REFUSED without sending if the Tsugite finds it from its own record, and it does not open.",
          "When the Primitive's open does not return ok: true (a tampered envelope, a different aad and so on), it stops with OPEN_REFUSED (including authentication failures), and when seal does not return ok: true, with SEAL_REFUSED.",
          "When the KMS Tsugite's result is unknown, it stops with KMS_UNKNOWN, when the Tsugite did not send, with KMS_REFUSED, when the Tsugite stopped because the receipt could not be matched or GENERATED lacks the wrapped key's reference or version, with KMS_HALTED, and when the result is not the desired state (DENIED, KEY_DISABLED, FAILED and so on), with KMS_DENIED.",
          "When there is no handle or the Tsugite does not answer exactly true that it is usable, it stops with HANDLE_NOT_USABLE, and when withDataKey throws, returns no result or does not pass a 32-byte DEK, with KEY_ACCESS_FAILED.",
          "When timestamp does not return a date-time string or throws, it stops with CLOCK_FAILED, and when the previous operation has not finished, with BUSY (only one operation at a time).",
          "Fuzzing that compares against a separately written reference confirmed every time that the opened plaintext is the same as the plaintext that made the envelope, and that the number of times it opened when something differed was 0 (mismatches and fail-open cases were 0)."
        ],
        "what_it_does_not_do": [
          "The Encryption at rest (envelope) part is not a Tsugite. It has no Request or Receipt of its own; the exchange with KMS is done by the bundled kms-envelope Tsugite under its own contract (kms-envelope/v1). All of those requests have intended_side_effect NONE, and this part itself does not touch the outside.",
          "The meaning of the bundled parts (being able to wrap or open is not permission to use) stays on each part's side.",
          "Having encrypted or opened data is not approval and is not authority (approval: false, authorization: NOT_DERIVED, plaintext_key_material: NOT_TRANSPORTED, derives_authority: false).",
          "It keeps neither the DEK nor the handle, and the results contain no key material. Its only state is a sequence number, which is lost on restart.",
          "It does not store wrapped keys or envelopes, rotate keys (B-05) or judge whether data may be used. Confining the DEK inside the port and zeroing it at expiry or at the end of the process are the port's role and have not been checked.",
          "It has never connected to a real KMS. The unit tests and fuzzing use stand-ins, and the wiring with the code of the real two parts was checked against a stand-in port that holds the DEK inside.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular KMS."
        ],
        "integration": "Create it with createEnvelopeEncryption(options) in src/envelope-encryption.js. Input: kms_key_ref, request_prefix, ports and an optional timestamp. Returns: { ok, errors, instance }; encrypt returns envelope, wrapped_key and encryption_context_digest, decrypt returns plaintext, and every result carries approval: false. User provides (injected through ports): kms (the client of the kms-envelope Tsugite), keyAccess.withDataKey, and seal and open (envelope-encrypt). Side effect: NONE; credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-04",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Encryption in transit (TLS/mTLS) — Encryption in transit (TLS/mTLS) configuration verification part (Primitive)",
        "summary": "The Encryption in transit (TLS/mTLS) configuration verification part (Primitive) is a standalone part that checks a TLS configuration object against rules R1 to R10 of a fixed profile (erabazu.primitive/tls-config/modern-2026/v1). It does not do TLS itself (it does not connect and does not read certificates). A value that does not meet the rules is stopped with a fixed code and position, and all violations are returned in a fixed order (fail-closed).",
        "problem": "For the problem of \"looking at the padlock icon and stopping there\", it checks the TLS configuration itself against versioned rules. It does not silently let through TLS 1.0 or 1.1 or SSL, weak ciphers, settings that do not check the peer's certificate or hostname, or certificate or key contents (PEM or base64 blobs) mixed into the configuration.",
        "use_when": [
          "You want to check a TLS configuration (min_version, max_version, ciphers_tls12, reject_unauthorized, verify_hostname, ca_refs, client_auth, alpn) against rules R1 to R10 before using it.",
          "In a configuration that allows TLS 1.2, you want to limit ciphers to a list of the names of 6 ECDHE AEAD ciphers (an allowlist).",
          "As a server-side mTLS setting that requires the peer's certificate (client_auth.required is true), you want to check that the references to the trust anchors (client_auth.ca_refs) are present.",
          "You want to receive the violations not one at a time but all together, in a fixed order (R1 → … → R10), with codes and positions (JSON Pointer).",
          "You want to call it locally, as processing that does not depend on the clock, random numbers, the environment's locale or outside state and that has no outside boundary. verify does not throw an exception and does not modify its input."
        ],
        "do_not_use_when": [
          "You want the encryption of the connection and mutual authentication themselves (a TLS connection). This part only checks the configuration and does not connect.",
          "You want to check certificates themselves (expiry, key length, signature algorithm, chain), revocation (CRL, OCSP), or that the peer's name matches.",
          "You want certificates issued or renewed, or the configuration applied to a server, a load balancer or cloud settings. Issuance and renewal are B-09, and applying the configuration stays on the Tsugite side.",
          "You want to check an OpenSSL cipher string as it is (separated by :, excluded with !, groups such as HIGH). It accepts only an array of cipher names.",
          "You want to decide TLS 1.3 cipher suites, key exchange groups (curves), signature algorithms, session resumption or 0-RTT. The profile does not decide them.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the configuration is not a plain object it stops with NOT_A_CONFIG, when there is a key that is not defined (including __proto__) with UNKNOWN_KEY, and when a required key is missing (including a value of undefined) with MISSING_KEY.",
          "For SSLv2, SSLv3, TLSv1 or TLSv1.1 it stops with VERSION_TOO_OLD, for any other incorrect version value with INVALID_VERSION, and when max_version is below min_version with VERSION_RANGE_INVERTED.",
          "When TLS 1.2 is allowed but ciphers_tls12 is missing it stops with CIPHERS_REQUIRED, and for an empty array or a cipher string with INVALID_CIPHER_LIST.",
          "For any cipher other than the 6 allowed ones (CBC, RC4, 3DES, NULL, anon, export, static RSA, DHE, TLS 1.3 names, differences in case or whitespace, holes in the array), it stops with CIPHER_NOT_ALLOWED.",
          "When reject_unauthorized is not true it stops with REJECT_UNAUTHORIZED_REQUIRED, and when verify_hostname is not true with VERIFY_HOSTNAME_REQUIRED (\"true\" and 1 are also violations).",
          "When ca_refs is empty it stops with INVALID_CA_REFS, when a reference has PEM markers with CA_REF_LOOKS_LIKE_PEM, when it is over 128 characters or contains +, = or a line break with CA_REF_LOOKS_LIKE_BLOB, and when it is not the prescribed sequence of characters with INVALID_CA_REF.",
          "When client_auth has the wrong shape it stops with INVALID_CLIENT_AUTH, when required is true but client_auth.ca_refs is missing or empty with CLIENT_CA_REFS_REQUIRED, when alpn is empty with INVALID_ALPN, and when an id is not printable ASCII with INVALID_ALPN_ID.",
          "When inspecting a value itself raises an exception (a Proxy trap, a getter that throws), it returns INSPECTION_FAILED and does not throw. Each getter is read only once per value, and the check is done on that copy."
        ],
        "what_it_does_not_do": [
          "The item's name refers to encryption of the connection and mutual authentication, but this part only checks the TLS configuration and does not include TLS connections or loading certificates. A configuration that meets the rules does not mean that a connection has been checked.",
          "It does not check certificates themselves (expiry, key length, signature algorithm, chain), revocation (CRL, OCSP), or that the peer's name matches.",
          "It has not been checked whether the execution environment (Node.js, OpenSSL, a load balancer and so on) actually enforces this configuration or accepts the way the names are written (the configuration is not passed to tls).",
          "It does not check what a ca_refs reference points to or whether it can be resolved. Because the check is on appearance, any string of up to 128 characters that matches the prescribed sequence of characters is accepted as a reference.",
          "The list of allowed ciphers is a judgement made on 2026-09-30. If they later become weak, this is corrected by raising the profile's version.",
          "A configuration that meets the rules does not mean it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for the TLS settings of a particular server or cloud."
        ],
        "integration": "Call verify(config) in src/tls-config.js (it does not throw). Input: a plain object with only 8 keys: min_version, max_version, ciphers_tls12, reject_unauthorized, verify_hostname, ca_refs, client_auth and alpn. Returns: { ok: true, errors: [] } or { ok: false, errors: [{ code, path }, ...] } (code is from error_taxonomy, path is a JSON Pointer; all violations are listed in rule order). Turning it into an actual configuration string is up to the caller. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-05",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "Key management and rotation — Key Management & Rotation Tsugite",
        "summary": "The Key Management & Rotation Tsugite (継手, tsugite) checks requests (KeyLifecycleRequest) for key operations on an external KMS or HSM (creating a key, creating a new version, which is rotation, changing the primary, disabling a version, re-enabling it, scheduling destruction, cancelling the schedule, and describing the state), carries them, and matches the returned receipt (KeyLifecycleReceipt) against the contract. It does not carry key material. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "It prevents sending a version disablement or a scheduled destruction without evidence of authority from a person or organization, making a version that is not ENABLED the primary, scheduling destruction with too short a waiting period, and mistaking a completed key operation for permission to use the key.",
        "use_when": [
          "You want to create keys on an external KMS or HSM, create new versions (rotation) and change the primary, with each operation checked against the contract before it is carried.",
          "You want to send version disablement and scheduled destruction only after receiving evidence of authority from a person or organization bound to that key, version and op. The evidence is used up by a single change.",
          "You want destruction only as a schedule with a waiting period (at least one day, and at least the configured minimum), not immediately, so that CANCEL_DESTROY can return the version to DISABLED.",
          "You want the version made primary to be limited to versions this Tsugite has matched as ENABLED."
        ],
        "do_not_use_when": [
          "You want to destroy a key immediately. The contract has no op for immediate destruction.",
          "You want to check the rotation policy (when to rotate, how many generations to keep). That is the role of a local Primitive and is not included in this Tsugite; rotation_policy_ref is only carried.",
          "You want to encrypt or sign with a key. Encryption is the role of the KMS envelope Tsugite (for B-03, a Component bundles it), and signing is the role of the B-01 KMS signing Tsugite.",
          "You want to pass key material (including public keys). This Tsugite does not carry key material, and public keys are references only.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a connection to a KMS or HSM that is ready to use. It has never connected to an actual KMS or HSM; the port implementation, credentials and configuration values will be settled at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks the configuration. If there is no configuration, or for a KMS or key spec not in the lists, a scheduled destruction without a minimum (min_destroy_window_seconds), or a waiting period shorter than the minimum or shorter than one day, it does not send.",
          "It checks the evidence of authority. With no evidence field, evidence not received, a different origin (including anything that is not HUMAN or ORGANIZATION), a subject digest for another key, version or op, or evidence already used up, it does not send a disablement or a scheduled destruction.",
          "It checks the new primary. For an unknown version, a version that is not ENABLED, or conflicting version records, it does not send SET_PRIMARY_VERSION.",
          "It checks the form of references and of the request. References containing drives, .., control characters, line separators, whitespace or PEM, a request_id that is not a Ref, an op not in the contract (including DESTROY), fields the op does not use, and missing fields that the op requires are not sent.",
          "It matches the receipt. A spoofed request_digest, a different request_id, KMS, key or version, a mismatched op, a state outside the table, an increased or unknown effect, and a receipt that refused yet has an effect are not accepted, and it stops (HALTED).",
          "Receipts that carry key material or a public key itself, and receipts that derive permission to use the key (authorization), are not accepted.",
          "It checks the destruction time. A destruction time earlier than the waiting period or in the past, a schedule without a time, a receipt that has a time although it is not DESTROY_SCHEDULED, and a CANCEL_DESTROY that does not return to DISABLED are not accepted.",
          "A send whose result is unknown is resolved by resending the same request (lookup), and nothing else is sent until it is resolved. A request with the same request_id but different content is not sent."
        ],
        "what_it_does_not_do": [
          "It does not create evidence of authority. Disabling a version and scheduling destruction require evidence from a person or organization outside the part as input (HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_KEY_DISABLE_AND_DESTROY). It does not read the content of the evidence (who issued it, its term, whether it is genuine) either.",
          "A completed key operation is neither permission to use the key nor business approval (authorization is NOT_DERIVED, approval: false).",
          "It does not store key material. This Tsugite carries requests for key operations; it neither carries nor holds key material, and it does not include deciding the rotation policy.",
          "Whether the KMS's own destruction waiting period is shorter than requested, whether destruction happened as scheduled, and whether a cancellation took effect are known only from the KMS's answer (DESCRIBE).",
          "That a reference field is not key material can be checked only by its form. A short encoded key string cannot be told apart from a reference.",
          "The record of version states exists only inside this Tsugite and is lost on restart. If someone else changes a version's state on the KMS, the record becomes outdated. Credentials are held by the port (HELD_BY_PORT).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It does not change the contract either (schema authority is NOT_SPECIFIED).",
          "It is not a template for using any specific cloud KMS or HSM key management API."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/key-lifecycle/v1. Input: pass a KeyLifecycleRequest to submit(request) on createKeyLifecycleClient({ cfg, port }); before a disablement or a scheduled destruction, pass the evidence with recordAuthorityEvidence. Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the KMS or HSM (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for a scheduled destruction, NONE for DESCRIBE, EXTERNAL_REVERSIBLE for the others.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-06",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "Trusted timestamp (RFC 3161) — Timestamp Tsugite",
        "summary": "The Timestamp Tsugite (継手, tsugite) checks the fields of a time token returned by a time-stamping authority (TSA) against what we requested (hash, nonce), the pinned TSA, and the limits on clock skew and accuracy, and does not trust it if any one does not match (fail-closed). It does not parse RFC 3161 DER; signature verification is done on the JSON of six fields.",
        "problem": "When showing when something existed, for contracts or inspections, it prevents trusting as is a time token swapped to another hash, a reused (replayed) token, a token from a TSA that is not pinned, or a time with too much skew or too coarse accuracy. It is a check so the returned time is checked before being passed on to provenance.",
        "use_when": [
          "You want to always confirm that the time token received from the TSA matches the hash we sent (message_imprint) and the nonce.",
          "You want to pin the TSA you use and check against limits you set on clock skew (max_skew_seconds) and accuracy (max_accuracy_seconds).",
          "You want to verify the signature over the token's fields with a pinned public key (PEM) (signature in DER form, sha256).",
          "You want to pass \"when it existed\" on to the provenance Kernel (provenance-facts)."
        ],
        "do_not_use_when": [
          "You want to pass in the RFC 3161 DER (ASN.1) token returned by the TSA as is and have it checked. This Tsugite does not parse DER. The token must be passed with its six fields already extracted, and signature verification also covers the JSON of those six fields.",
          "You also want it to request timestamps from the TSA, or to fetch and pin the TSA certificate. Communication with the TSA is not included (no port).",
          "You want to issue time tokens. signToken in src/tsa.js is a test stand-in for a TSA, not a real TSA.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the imprint. If the token's message_imprint does not match the hash we sent (substitution), it is not trusted.",
          "It checks the nonce. If our nonce is not echoed back unchanged (replay), it is not trusted.",
          "It checks the TSA. A token from a TSA (tsa_id) that is not pinned is not trusted.",
          "It checks the time. A token whose skew from the reference time exceeds the limit, whose accuracy (accuracy_seconds) exceeds the limit, or whose gen_time is not an integer is not trusted.",
          "It checks the hash algorithm. An algorithm that has not been adopted is not trusted.",
          "It checks the signature. verifyToken does not trust the token unless the signature_valid passed in is true. verifyTokenSignature returns false for a tampered token or a signature by another key, as it cannot be verified with the pinned public key."
        ],
        "what_it_does_not_do": [
          "It does not parse the RFC 3161 DER (ASN.1) form. What is signed is the JSON of the token's six fields (message_imprint, hash_algo, tsa_id, nonce, gen_time, accuracy_seconds).",
          "It does not communicate with an actual TSA, and does not fetch or pin the TSA certificate. These are decided at deployment.",
          "verifyToken does not verify the signature itself. It receives signature_valid (a boolean) and decides on it. Signature verification is done separately by verifyTokenSignature.",
          "The tests use an ECDSA P-256 key and a test stand-in for a TSA (signToken). No actual TSA was called.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a TSA API."
        ],
        "integration": "Input: call verifyToken(cfg, request, token, refTime) in src/guards.js with request { hash, nonce, hash_algo }, token { message_imprint, hash_algo, tsa_id, nonce, gen_time, accuracy_seconds, signature_valid } and the reference time refTime (in seconds). Returns: { ok, errors }. Separately, verifyTokenSignature(pinnedPubPem, fields, derSignature) in src/tsa.js verifies the signature over the six fields and returns a boolean. Not included: parsing RFC 3161 DER, communicating with the TSA, and fetching and pinning the TSA's certificate (decided at deployment). There is no port.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-07",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Hashing and content addressing — Hashing and content addressing part (Primitive)",
        "summary": "The Hashing and content addressing part (Primitive) is a standalone part that creates and verifies the sha256 of bytes, the typed digest used in contracts (domain, algorithm, value, target), and content addresses (sha256:<hex>). It accepts bytes only. Verification checks the shape, the algorithm, the value (by constant-time comparison), and the expected domain and target, and returns { ok, errors } without throwing an exception (fail-closed).",
        "problem": "When a unique fingerprint is created from content and verified as the basis for provenance and reconciliation, it prevents confusing bytes with strings, accepting a digest of the wrong shape or with a different algorithm, and verifying against the wrong domain or target.",
        "use_when": [
          "You want to create a sha256 (64 lowercase hex digits) or a content address (sha256:<hex>) from bytes.",
          "You want to create and verify the typed digest used in contracts ({ domain, algorithm: \"sha256\", value, target }). domain and target are labels and are not mixed into the hash.",
          "When verifying a digest against bytes, you want to pass the expected domain and target so that a mix-up is stopped.",
          "You need verification that does not throw. verifyDigest and parseContentAddress return { ok, errors } and do not throw an exception.",
          "You want the digest of a JSON value. Turn it into canonical bytes with the I-08 part first, then pass them to this part.",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no outside boundary and no state."
        ],
        "do_not_use_when": [
          "You want to pass a string as it is. This part does not accept strings (NOT_BYTES). The caller turns a string into bytes with a fixed character encoding (such as UTF-8) before passing it.",
          "You want to prove origin by a digest match. A match is not proof of origin. Signature verification is B-02.",
          "You need an algorithm other than sha256.",
          "You need a size limit on the bytes. It has no limit; a size limit is decided by the I-06 part or by the caller.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the input is not bytes (Uint8Array), such as a string, a number, an array, an ArrayBuffer or another typed array, it stops with NOT_BYTES.",
          "When the domain has the wrong shape it stops with INVALID_DOMAIN, and when the target is empty, not a string, or longer than 1024 characters it stops with INVALID_TARGET.",
          "When the digest is not an object with exactly the four keys (an extra key, a missing key, or no digest at all), it stops with NOT_A_DIGEST.",
          "When the algorithm is not sha256 it stops with UNSUPPORTED_ALGORITHM, and when the value is not 64 lowercase hex digits (uppercase, or the wrong length) it stops with MALFORMED_VALUE.",
          "When the sha256 of the bytes differs from the value, it returns VALUE_MISMATCH, using constant-time comparison (crypto.timingSafeEqual).",
          "When the domain or target differs from what was expected it stops with DOMAIN_MISMATCH or TARGET_MISMATCH, and when expect is not an object it stops with INVALID_EXPECTATION.",
          "When a content address is not sha256:<64 lowercase hex digits>, it stops with MALFORMED_CONTENT_ADDRESS."
        ],
        "what_it_does_not_do": [
          "It does not turn strings into bytes and does not canonicalize JSON. The caller turns a string into bytes, and a JSON value is turned into canonical bytes with the I-08 part before it is passed in.",
          "A digest match is not proof of origin. It does not include signature verification (B-02) or key management.",
          "A matching digest does not mean that the content is correct, nor that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "It does not handle algorithms other than sha256.",
          "The digests.js copies kept inside the Tsugite (19 of them) have not yet been replaced with this part; they still have their own copies. For bytes, the value is the same byte for byte as the copies' sha256hex, but the copies also accept strings and hash them as UTF-8, while this part accepts bytes only. Any replacement is separate work, done after the compatibility table has been checked.",
          "It does not decide the request canonicalization plus sha256 (requestDigestValue), nor which fields go into a digest. Those stay with the caller (each Tsugite or contract).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular hashing library."
        ],
        "integration": "Call verifyDigest(digest, bytes, expect) in src/digest.js (it does not throw). Input: a digest with exactly the four keys, bytes (a Uint8Array, including a Buffer), and an optional expect ({ domain, target }). Returns: { ok: true, errors: [] } or { ok: false, errors: [{ code }] } (code is from error_taxonomy). sha256Hex, digest and contentAddress throw DigestError on a value they cannot accept; parseContentAddress does not throw. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependency is the Node.js built-in crypto.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-08",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Random numbers and nonces (CSPRNG) — Random number and nonce part (Primitive)",
        "summary": "The Random number and nonce part (Primitive) is a thin wrapper over the OS CSPRNG (Node.js crypto.randomBytes). It produces randomBytes(n) (1 to 1024 bytes), nonces (hex or base64url), and randomInt(maxExclusive) (uniform, for ranges from 1 to 2^48). What it handles is only argument checking, the output format and the rejection sampling in randomInt; the quality of the randomness itself is left to the OS and Node.js crypto.",
        "problem": "It fixes how random numbers are produced, a backstage function where \"if it is weak, everything collapses\". It fixes the source to the OS CSPRNG so that it cannot be swapped out, accepts no seed from the caller, checks length, encoding and range, and makes randomInt uniform by rejection sampling, which does not create the bias of x mod m. When the source fails, it produces no value and stops (fail-closed).",
        "use_when": [
          "You want to draw bytes of a fixed length (1 to 1024 bytes) from the OS CSPRNG.",
          "You want to produce a nonce as a string in lowercase hex or in unpadded base64url (the RFC 4648 alphabet for URLs and filenames).",
          "You want to produce an integer from 0 up to but not including max_exclusive without bias, for ranges from 1 to 2^48 (2^48 included).",
          "You want to ensure at the entrance that no caller seed gets mixed in. A seed field is refused with UNKNOWN_FIELD.",
          "You need a way of calling it that does not throw. generate(request) returns { ok, errors, value } and does not throw an exception."
        ],
        "do_not_use_when": [
          "You want the quality of the randomness (unpredictability, lack of bias) to be checked. This part has not checked it and leaves it to the OS and Node.js crypto.randomBytes.",
          "You want this part to judge what length is enough for your purpose. The caller decides the length, and a 1-byte nonce is also accepted.",
          "You need reproducible (deterministic) values. Each call draws from the OS CSPRNG, and values cannot be reproduced (by design).",
          "You want more than 1024 bytes in one call, integers over a range larger than 2^48, or numbers as bigint or strings.",
          "You use it in forked processes, cloned virtual machines and the like. The behaviour of the OS CSPRNG there has not been checked.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the request is not a plain object (an array, a Map, a class instance and so on), it stops with NOT_A_REQUEST.",
          "When kind is none of \"bytes\", \"nonce\" and \"int\" (including prototype names) it stops with UNKNOWN_KIND, and when the request has fields other than the fixed ones (including seed) with UNKNOWN_FIELD.",
          "When length or bytes is not an integer (number) from 1 to 1024 (0, over 1024, a fraction, a string, a bigint), it stops with INVALID_LENGTH.",
          "When encoding is neither \"hex\" nor \"base64url\" it stops with INVALID_ENCODING, and when max_exclusive is not an integer (number) from 1 to 2^48 with INVALID_BOUND.",
          "When the random source throws, returns a value of the wrong length or a value that is not bytes, or when randomInt discards 64 times in a row, it produces no value and stops with ENTROPY_UNAVAILABLE.",
          "When inspecting the request itself raises an exception (a getter, a Proxy trap and so on), generate returns INSPECTION_FAILED. Each field is read only once, and only the checked values are used."
        ],
        "what_it_does_not_do": [
          "The item's name refers to \"generating unpredictable values\", but this part has not checked the quality of the randomness (unpredictability, lack of bias). It leaves that to the OS and Node.js crypto.randomBytes. The statistical test is a smoke test that looks for large bias; it is not a formal statistical test such as NIST SP 800-22.",
          "It does not judge how many bytes to use for which purpose (uniqueness of nonces, strength of tokens). The caller decides.",
          "Source failure (ENTROPY_UNAVAILABLE) was checked with a substituted source. It has not been checked by making the actual OS CSPRNG fail.",
          "It accepts no caller seed and no means of swapping the source. The public entry's module.exports is frozen, and the internal makeRandom is not exported.",
          "Producing a value does not mean that the length is enough for the caller's purpose, nor that the quality of the randomness has been confirmed by a formal test (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular random number library."
        ],
        "integration": "Call generate(request) in src/random.js (it does not throw). Input: one of { kind: \"bytes\", length }, { kind: \"nonce\", bytes, encoding } or { kind: \"int\", max_exclusive }. Returns: { ok: true, errors: [], value } (a Buffer, a string or an integer) or { ok: false, errors: [{ code }] } (code is from error_taxonomy). randomBytes, nonce and randomInt throw RandomError on a value they cannot accept. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are the Node.js built-in crypto and util.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-09",
        "category": "B/暗号・信頼",
        "kind": "Tsugite",
        "title": "Certificate issuance and renewal (ACME) — Certificate Issuance & Renewal (ACME) Tsugite",
        "summary": "The Certificate Issuance & Renewal (ACME) Tsugite (継手, tsugite) checks requests (AcmeRequest) to an ACME (RFC 8555) certificate authority (placing an order, NEW_ORDER, where a renewal is a new order with renewal_of_ref; listing challenges; notifying that a response is ready; checking the order status; requesting issuance, FINALIZE; and retrieving the certificate), carries them, and matches the returned receipt (AcmeReceipt). It does not treat a successful issuance as rightful ownership of the names or as trust (fail-closed).",
        "problem": "It prevents requesting an irreversible issuance (FINALIZE) with a CSR whose names do not match the order or without evidence from a person or organization, and using a received certificate whose names or public key differ. Because issuance is publicly recorded in Certificate Transparency, it checks both before sending and after receiving.",
        "use_when": [
          "You want requests for obtaining TLS certificates from an ACME certificate authority and for renewing them (a new order with renewal_of_ref) to be checked against the contract before they are carried.",
          "You want to send an issuance request only after receiving evidence from a person or organization bound to this directory, order, CSR and name subject (authority_subject_digest).",
          "You want to request issuance only after confirming that the CSR's names are exactly the order's names and that the sha256 of the CSR content and of its public key match the digests in the request.",
          "You want to accept a retrieved certificate only after reading back the chain and matching its names and public key against the order and the CSR."
        ],
        "do_not_use_when": [
          "You want to revoke a certificate. Revocation (REVOKE) is not in the v1 contract and needs a separate decision.",
          "You want to place challenge responses (an HTTP file or a DNS record). That is the role of a separate port; this Tsugite only returns references and digests for the challenges.",
          "You want it to decide when to renew or whether a certificate has expired. This Tsugite only checks the form and ordering of the validity period; it does not make that decision.",
          "You want to check the TLS configuration. That is the role of a local Primitive (B-04).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a connection to a certificate authority that is ready to use. It has never connected to an actual certificate authority; the port implementation, the account key binding (JWS signing), nonces and configuration values will be settled at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks the configuration. If there is no configuration or a list is empty, or for a directory, account key or name not in the lists, a wildcard that does not cover the name, or an order whose names are not DNS names, are uppercase, overlap, are empty or are not in ascending order, it does not send.",
          "It checks the evidence for issuance. With evidence not received, a different origin (including anything that is not HUMAN or ORGANIZATION), or evidence bound to a different order, CSR, set of names or name order, it does not send FINALIZE.",
          "It checks the CSR. If the CSR cannot be read, the sha256 of its content or public key differs from the digest, names are missing, extra or different, the CN is outside the order, or the CSR has two SANs or two extensionRequests, it does not send FINALIZE.",
          "It checks the order sequence. An unknown order, an order whose directory or account key has changed, a notification for an order or challenge that is not pending, FINALIZE for an order that is not ready, and retrieval for an order that is not valid or that this Tsugite did not FINALIZE are not sent.",
          "It checks the form of references and of the request. References containing .., drives, control characters or line separators, an account key reference containing whitespace, a field for the account key's private key, REVOKE, fields of other ops, and a request_id that is not a Ref are not sent.",
          "It matches the receipt. A spoofed request_digest, a different op, directory or order, a receipt that derives name ownership or trust, an effect that is increased, understated or unknown, and a step backwards in the order or challenge state are not accepted, and it stops (HALTED).",
          "It matches the certificate. If leaf_names are missing, extra or different, the public key differs from the CSR's, the chain cannot be read back or its sha256 differs, the validity period differs, or the claims disagree with the leaf that was read back, it does not mark the certificate as matched.",
          "When reading DER, it does not accept non-minimal lengths, high tag numbers or trailing bytes."
        ],
        "what_it_does_not_do": [
          "It does not create the evidence for issuance. FINALIZE requires evidence from a person or organization outside the part as input (HUMAN_OR_ORGANIZATION_EVIDENCE_FOR_CERTIFICATE_ISSUANCE). It does not check whether the evidence record really came from a person or organization either.",
          "A successful issuance or retrieval is neither rightful ownership of the names nor trust (name_ownership and trust_decision are NOT_DERIVED, approval: false). It does not check that the domain is owned.",
          "It does not run automatic certificate acquisition or renewal by itself. This Tsugite carries ACME requests; it does not include deciding when to renew, placing challenge responses, or revocation (REVOKE).",
          "It does not check the CSR's self-signature, or the certificate's signature, chain, revocation or trust. The leaf is taken to be the first certificate in the chain.",
          "The CSR and the certificate are read by a small reader that reads only the DER it needs, and the tests used only P-256 CSRs and certificates built locally. It has not been checked against certificates issued by an actual certificate authority. If a certificate authority changes how the public key is encoded, even the same key will not be marked as matched.",
          "Order states and received evidence exist only inside this Tsugite and are lost on restart. Names are handled only as lowercase ASCII. Credentials such as the account key are held by the port (HELD_BY_PORT).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It does not change the contract either (schema authority is NOT_SPECIFIED).",
          "It is not a template for an ACME client of any specific certificate authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/acme-certificate/v1. Input: pass an AcmeRequest to submit(request, csr) on createAcmeClient({ cfg, port }) (csr only for FINALIZE); before FINALIZE, pass the evidence with recordAuthorityEvidence. Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the certificate authority (submit and readChain, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for FINALIZE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-11",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Merkle tree (root, inclusion proof) — Merkle tree (root, inclusion proof) part (Primitive)",
        "summary": "The Merkle tree (root, inclusion proof) part (Primitive) is a standalone part that builds a root from a sequence of bytes leaves using the Merkle Tree Hash (SHA-256) of section 2.1 of RFC 9162 (Certificate Transparency v2), and creates and checks inclusion proofs and consistency proofs. No operation throws; each returns { ok, errors }. Malformed input is stopped with fixed codes, and a mismatch in verification is returned as verified: false (not an error).",
        "problem": "It is a foundation for later checking the content of something that was \"bundled and signed\". If the root of the bundled tree is signed, an inclusion proof can confirm, partially, that a given leaf is included, and a consistency proof can confirm that the tree after appending has the earlier tree as its beginning. The leaf and node prefixes (0x00 and 0x01) prevent forged leaves that confuse leaves with nodes.",
        "use_when": [
          "You want to build the root of an RFC 9162 tree (64 lowercase hex digits) from a sequence of bytes leaves (merkleRoot).",
          "You want to create and check an inclusion proof (audit_path) showing that a leaf is included in a root (inclusionProof, verifyInclusion).",
          "For a record that grows by appending, you want to create and check a consistency proof showing that the old tree is the beginning of the new tree (consistencyProof, verifyConsistency).",
          "You want verification mismatches (a mixed-up proof, leaf, index or root; a missing or extra proof element) returned as verified: false rather than as an exception.",
          "You want to call it locally as deterministic processing that has no clock, no random numbers and no outside connection. There are no dependency packages."
        ],
        "do_not_use_when": [
          "You need a transparency log. This part does not store or append to a tree, and does not handle signed tree heads (STH), log monitoring, gossip or log IDs.",
          "You want to determine the size of the larger tree from the proof alone. A different size whose proof path has the same shape can also give true. Take the size and the root from a signed record.",
          "You need a hash other than SHA-256, or you want to check proofs for a tree or log larger than 1,048,576 leaves (BAD_SIZE).",
          "You want to confirm consistency with old_size equal to 0, or consistency between trees of the same size (old_size = new_size). It stops with BAD_SIZE. For the same size, the caller compares the roots directly.",
          "You need a tree that duplicates the rightmost node when the count is odd (the Bitcoin style). This part does not duplicate; it promotes the rightmost node unchanged to the level above.",
          "You need speed for creating proofs many times over a large tree. Each time it creates a proof, it rebuilds the tree from the leaves it is given (O(n)), and that speed has not been measured. It also has no limit on the total bytes of the leaves; memory and time are the caller's responsibility.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with NOT_BYTES_LIST when leaves is not an array, when an element is not bytes (Uint8Array) (including holes, getters, other typed arrays, ArrayBuffer, DataView and bytes wrapped in a Proxy), or when the argument is not an object.",
          "It stops with TOO_MANY_LEAVES, before reading the elements, when there are more than 1,048,576 leaves, and with LEAF_TOO_LARGE, before hashing, when a single leaf is larger than 64 MiB.",
          "It stops with INDEX_OUT_OF_RANGE when index or leaf_index is not an integer, is negative, or is not less than the tree size. In verifyInclusion too, this is the stricter reading that treats it as an error rather than as a verification failure.",
          "It stops with BAD_SIZE when tree_size is not an integer from 1 to 1,048,576, or when old_size and new_size are not integers with 0 < old_size < new_size ≤ 1,048,576.",
          "It stops with BAD_HEX when root, old_root, new_root or a proof element is not a string of 64 lowercase hex digits, and with BAD_PROOF_SHAPE when audit_path or proof is not an array.",
          "It does not pass rewritten, missing, added or reordered proof elements, or a mixed-up leaf, index or root (verified: false). A proof it has created always gives true with the same root and size.",
          "When it stops, it returns only the one code it finds first, in a fixed order. It does not call getters, and it does not throw even for values that raise an exception when inspected (a Proxy trap and so on)."
        ],
        "what_it_does_not_do": [
          "It does not sign (signing the root is B-12). It does not prove time either (B-06). It does not say whose root it is or when it is from.",
          "verified: true only means that, as long as there is no SHA-256 collision, the given leaf can be said to be included in the tree with the given root and size (or that the old tree is the beginning of the new tree). It does not show that the root is correct, that the content is correct, or that it may be accepted.",
          "It does not store trees, proofs or records. It keeps no state between calls; storing records and managing appends remain with the caller (H-11 and so on).",
          "Comparison is not constant-time (on the premise that roots and proofs are not secret). Its strength relies on the difficulty of finding SHA-256 collisions and preimages.",
          "The roots for 1 to 8 leaves from the certificate-transparency tests are values copied into the tests; they were not checked against the original files fetched for that purpose (they were confirmed by matching values built by hand from the definition).",
          "Roots, proofs and verification results are not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular transparency log (a CT log, Trillian and so on)."
        ],
        "integration": "Call merkleRoot, inclusionProof, verifyInclusion, consistencyProof and verifyConsistency in src/merkle.js (none of them throw). The argument is an object holding leaves, index, leaf, leaf_index, tree_size, audit_path, root, old_size, new_size, old_root, new_root and proof; leaves are Uint8Array, and roots and proof elements are 64 lowercase hex digits. It returns { ok: true, errors: [], ... } or { ok: false, errors: [{ code }] }. There are no side effects, no state, no outside connection and no credentials, and there is no port for the caller to provide. Its only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-12",
        "category": "B/暗号・信頼",
        "kind": "Primitive",
        "title": "Signing with a local key (Ed25519) — Signing with a local key (Ed25519) part (Primitive)",
        "summary": "The Signing with a local key (Ed25519) part (Primitive) is a standalone part that signs bytes with an Ed25519 private key held by the caller (a Node.js KeyObject), and returns the signature (unpadded base64url) and the sha256 of the SPKI DER of the derived public key (the fingerprint). It accepts the private key only as a KeyObject, and does not export, store or return it. No operation throws; each returns { ok, errors } (fail-closed).",
        "problem": "Behind signing a record, how the key is handled is not visible. This part refuses uses that pass a private key around as bytes, PEM, JWK and so on, and accepts the private key only as a KeyObject. Keys and signatures of the wrong form are stopped with fixed codes, and when it stops, it returns no signature.",
        "use_when": [
          "You want to sign bytes (up to 16 MiB) with an Ed25519 private key held by the caller (sign). What to sign (a Merkle tree root, canonical bytes and so on) is decided by the caller.",
          "You want to show which key signed by the sha256 of the public key's SPKI DER (public_key_spki_sha256) (sign, publicKeyFingerprint).",
          "You want a round-trip check right after signing (verify; if it does not match, verified: false, which is not an error).",
          "You need the same signature every time from the same key and the same message (pure Ed25519 of RFC 8032; it uses no clock and no random numbers).",
          "You want to sign with a local key without using a KMS or HSM. This part does not talk to a KMS, an HSM or a key vault."
        ],
        "do_not_use_when": [
          "You want to verify a signature with a key the caller has pinned. verify is a round-trip check and is not a substitute for that. Verification with a pinned key is B-02.",
          "You need key storage, key rotation or revocation, or signing in an HSM or KMS. This part does not handle them.",
          "You want to pass the private key as bytes, a string, PEM, JWK or a WebCrypto CryptoKey. It stops with KEY_NOT_KEYOBJECT.",
          "You want to sign with a type of key other than Ed25519 (EC, RSA, X25519, Ed448, a symmetric key). It stops with KEY_TYPE_UNSUPPORTED.",
          "You want to sign a message larger than 16 MiB. It stops with MESSAGE_TOO_LARGE.",
          "You need resistance to side channels (timing, power). This has not been checked.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_ARGUMENT when the argument is not a plain object or has fields other than the fixed ones. When reading the argument itself raises an exception (a getter or a Proxy trap), it is INSPECTION_FAILED.",
          "It stops with NOT_BYTES when message is not a Uint8Array, and with MESSAGE_TOO_LARGE when it is larger than 16 MiB.",
          "It stops with KEY_NOT_KEYOBJECT when the key is not a KeyObject (PEM, DER, JWK, bytes, a CryptoKey, something imitating a KeyObject, a Proxy of a KeyObject, a missing field).",
          "It stops with KEY_TYPE_UNSUPPORTED when the key is a KeyObject but of the wrong type (a public key for sign, a private key for verify, a symmetric key, EC, RSA, X25519, Ed448).",
          "It stops with INVALID_KEY_ID when key_id is not a Ref (1 to 200 printable ASCII characters excluding white space, not containing \"..\", and not starting with / or \\).",
          "It stops with INVALID_SIGNATURE when signature is not a string of canonical unpadded base64url encoding 64 bytes (padding, standard base64, non-zero leftover bits, a wrong length).",
          "A signature with one bit flipped, an S + L signature, and a signature over another message or by another key give verified: false.",
          "It reads each field of the argument only once, and uses only the value it has checked even if a getter returns a different value."
        ],
        "what_it_does_not_do": [
          "A signature shows only that \"someone holding this private key signed these bytes\". It is not proof of approval, that the content is correct, that it may be accepted, or who the signer is. Deciding who may sign is outside this part.",
          "This part does not export the private key (it does not call export), does not store it, does not write it to logs and does not put it in results. However, it does not erase the KeyObject the caller passed (erasing it is left to GC).",
          "key_id is the caller's label; it does not check that it is tied to the key. To confirm which key it is, use public_key_spki_sha256.",
          "The Ed25519 computation itself is left to the Node.js crypto module (OpenSSL). What was checked is TEST 1 to 3 in section 7.1 of RFC 8032, and cross-checks against Node's crypto with keys created at run time.",
          "The strictness of verify (small-order public keys, non-canonical points and so on) follows OpenSSL, and boundaries other than the S + L signature have not been checked. It has not been cross-checked against B-02 either.",
          "Signatures, fingerprints and verification results are not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular signing library."
        ],
        "integration": "Call sign({ message, private_key, key_id }), publicKeyFingerprint({ public_key }) and verify({ message, public_key, signature }) in src/local-sign.js (none of them throw). sign returns { ok: true, errors: [], algorithm: \"Ed25519\", key_id, signature, public_key_spki_sha256 } (signature is 86 characters of base64url). When it stops, it returns { ok: false, errors: [{ code }] }. The caller creates and holds the KeyObject. There are no side effects, no state and no outside connection, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "B-13",
        "category": "B/暗号・信頼",
        "kind": "Adopted",
        "title": "Countersignature verification (.amupkg) adopted part (Adopted)",
        "summary": "The Countersignature verification (.amupkg) adopted part (Adopted) is a part that, before an .amupkg is passed to the Runtime, checks the manifest, the self signature and the wi-t.com countersignature against the keys and revocations of the trust bundle from a fixed root (root-usb-1), and returns PASS or REFUSE. It is a part made by another writer, taken in without rewriting the bytes of the original version (33f2b1c); what ERABAZU added is only this manual and the workshop fuzzing.",
        "problem": "Behind \"verified\", which keys and revocations were looked at is not visible. This part makes anything with a missing or incorrect countersignature REFUSE and does not let it through (fail-closed), fixes the real keys, the fetch locations and the root inside the part so that arguments cannot replace them, and refuses a rollback of the trust and different bytes under the same generation. The result carries the trust it looked at and the digest of the manifest as facts.",
        "use_when": [
          "You want to check the integrity, the self signature, the countersignature, the trust, revocation and the binding of the bytes of an .amupkg (a Uint8Array) before passing it to the Runtime, and receive verdict (PASS or REFUSE), code, stage and facts (verifyAmupkg; it does not throw).",
          "You want to receive the value for checking, when the package is opened in the Runtime after PASS, whether the returned amupkg_digest equals sha256: + facts.manifest_digest. This comparison is done by the caller.",
          "You want to fetch the trust bundle and save only what passed the checks to the store port (refreshTrust). When it cannot be fetched, it keeps judging with the saved one or with gen 10 built into the part.",
          "You want to tell apart how fresh the trust is. New admissions work until the trust's next_update, and already admitted ones work until 30 days after next_update; after that it stops with COUNTERSIG_TRUST_STALE or COUNTERSIG_TRUST_EXPIRED.",
          "You want to keep a record of admission only for what passed (recordAdmitted), and see the trust, the number admitted and the verifier version (status).",
          "You need a part whose taken-in bytes can be checked against the original commit (33f2b1c) and the sha256 of its 36 files."
        ],
        "do_not_use_when": [
          "You want to let an .amupkg brought in by a user through with the fixed samples (pinnedDigests). The fixed samples are protected by the code signature of the caller's program, and the part does not check it. pinnedDigests cannot be used for an .amupkg brought in by a user.",
          "You want to treat it as a result checked in the workshop that a countersignature signed with the real key PASSes. The workshop has not checked this (the workshop's tests and fuzzing use only a disposable root and keys).",
          "You want to use PASS as a mark of approval, of permission to sell, or of the content being correct. PASS means only that the origin, integrity and scope of review were checked.",
          "You want to restrict the ZIP file to exactly the same bytes as a whole. PASS is decided by the manifest, the files listed in it and the signatures, so for that use, use facts.archive_digest (the sha256 of the whole ZIP).",
          "You want to use the text shown to users (title_ja and detail_ja in display) as final text as it is. display is provisional text.",
          "You want a newer version to be used automatically when the original is fixed. It does not follow automatically (auto_follow: false). A newer version is copied again and inspected again. It is also inspected again when a new generation of the trust (gen 11 or later) is issued.",
          "You use it on a version other than Node.js v24.21.0. The original is an ES module for Node.js 20 or later, but the workshop has not checked other versions (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "The order of checks is: integrity, then that the signature files are not in the manifest, the self signature, the fixed samples, the countersignature file, the form of the countersignature, the trust (root and freshness), the countersignature key and signature, revocation, and the binding. When it stops, it returns code and stage (integrity, self_signature, countersignature, trust, pinned).",
          "It stops with PKG_ARCHIVE_UNSAFE when the ZIP exceeds the limits (96 MiB, 2000 entries, 64 MiB per entry, 192 MiB in total, a ratio of 100), has unsafe names or cannot be read; with AMUPKG_SECTION_INVALID when it does not follow the rules on allowed files and sections; with AMUPKG_DIGEST_MISMATCH when the sha256 in the manifest or a section digest does not match; with AMUPKG_PROFILE_UNSUPPORTED when the manifest profile cannot be handled; and with MANIFEST_LISTS_SIGNATURE_FILE when a signature file is listed in the manifest.",
          "It stops with SELF_SIGNATURE_MISSING when there is no self signature, with SELF_SIGNATURE_INVALID when it does not match the manifest, and with SELF_KEY_REVOKED when the self key is revoked.",
          "It stops with its own code when the countersignature file is missing (COUNTERSIG_MISSING) or unreadable (COUNTERSIG_UNREADABLE), when the schema or purpose differs, when it has an expiry (the rule is that it has none), when the scope of review (scope_id, scope_version) is missing, when the countersigner is not wi-t.com, or when it is not bound to the self key and self signature of this .amupkg (COUNTERSIG_SELF_SIGNER_MISMATCH).",
          "It returns REFUSE when the countersignature key is not in the trust bundle (COUNTERSIGNER_KEY_UNKNOWN), is not dedicated to countersigning, uses the wrong algorithm or is revoked, when the countersignature does not match (COUNTERSIG_SIGNATURE_INVALID), and when that countersignature is revoked (COUNTERSIG_REVOKED).",
          "It stops with COUNTERSIG_TRUST_UNAVAILABLE when the trust bundle cannot be checked (it cannot be fetched, and neither the saved one nor the built-in one can be used). Fetching the trust is limited to 256 KiB per file and 10 seconds, and redirects are not followed. A rollback of the trust and different bytes under the same generation are refused.",
          "Fuzzing of 1,000,000 runs in the workshop inspection, which changes a correct .amupkg in one way per run (rewriting, removing or adding a file covered by the manifest, replacing the countersignature, re-creating a signature with another key, rewriting random bytes and so on) and compares the result with the known answer, found 0 mismatches and 0 fail-open cases (Node.js v24.21.0)."
        ],
        "what_it_does_not_do": [
          "PASS means only that the origin, integrity and scope of review were checked; it is not an approval, a permission to sell, or a statement that the content is correct (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "The part checks only the manifest, the files listed in it, and the signatures. Bytes in the ZIP outside its files (such as trailing bytes) are outside what is checked, and the Runtime does not use them either. When you want the file's bytes to be identical as a whole, use facts.archive_digest (which changes when bytes are appended at the end).",
          "The fixed samples (pinnedDigests) are protected by the code signature of the caller's program, and the part does not check it. pinnedDigests cannot be used for an .amupkg brought in by a user.",
          "The production trust bundle (gen 10) carries the countersignature key wi-t-countersigner-ed25519.v1, and a countersignature not signed with that key gives COUNTERSIG_SIGNATURE_INVALID. The trust built into the part is also gen 10. However, the workshop has not checked that a countersignature signed with the real key PASSes. The workshop's tests and fuzzing do not use the real private key; they use only a disposable root and keys.",
          "This is not a verification implementation made by ERABAZU. It is a part made by another writer, taken in without rewriting its bytes (checked against the sha256 of 36 files and the record of the original provenance). The content, the order of checks, the stop codes and the API remain as in the original design. The files taken in also include LICENSE, LICENSES/CC0-1.0.md and NOTICE; NOTICE was added by the original writer.",
          "The end-to-end flow with the Runtime executable was not run in the workshop inspection (there is no executable). It passed in the CI of the original repository (run 37588955237). The original tests are 54 (53 pass, 1 skip).",
          "It writes the trust and the admission records to the store port, and reads the four trust files through the fetch port (LOCAL_REVERSIBLE). It keeps no state in the part's memory and uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Create an instance with createCountersignatureVerifier({ store, fetch?, now?, log?, pinnedDigests? }) in src/index.mjs (an ES module) and call await instance.verifyAmupkg(bytes, { admit? }). store (read, write) is a required port; fetch (null means it does not fetch), now and log can be left out. It returns { verdict, code, stage, facts, display }. It throws a TypeError only when the factory arguments are malformed or when bytes is not a Uint8Array. Its only dependencies are the Node.js built-ins (node:crypto, WebCrypto).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": [
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/bytes.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/canonical-json.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/purposes.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/roots.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-index.mjs",
              "license": "MPL-2.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/bundle.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/publishers.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/trust-v1/revocations.sig.json",
              "license": "CC0-1.0"
            },
            {
              "path": "vendor/kokoroamu-trust-f99fdbe/verify-core.mjs",
              "license": "MPL-2.0"
            }
          ]
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-01",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Object storage — Object Store Tsugite",
        "summary": "The Object Store Tsugite (継手, tsugite) relays saving (put) and reading (get) of files and blobs to an external object backend. put sends only checked requests and matches the receipt the backend returns (ObjectReceipt) as the contract requires. get computes the digest of the returned bytes itself and hands over the bytes only when the digest matches the expected value and the receipt also matches. If anything does not match, it does not accept (fail-closed).",
        "problem": "It prevents ending at \"it was saved\", that is, trusting the backend's receipt or claims as is and treating corrupted content or spoofed stored objects as correct. Content read back is not treated as verified just because the backend claims it matched; this Tsugite computes the digest itself and compares.",
        "use_when": [
          "You want to save files or blobs to an external object backend and read them back.",
          "You want to confirm content read back with a digest you computed yourself, not with the backend's claim, before using it.",
          "You want to set the accepted content-types and a size limit, and not let through content with a spoofed kind (content that does not match its magic bytes).",
          "You do not want the same content stored twice, or you want to protect targets that are never overwritten once written (WORM). When resending before a receipt is settled, it is resolved by a lookup.",
          "You want saving to stop until a person checks it when a partial write or a spoofed receipt appears."
        ],
        "do_not_use_when": [
          "You want to handle content-types other than application/pdf, image/png, image/jpeg, text/plain and application/json with their content kind confirmed. Only these 5 types can be confirmed by magic bytes, and types that cannot be confirmed are not let through.",
          "You need delete or lifecycle operations for retention/TTL (C-08) or archiving (C-09). They are not included in this Tsugite (later work).",
          "You want to treat a successful put as \"the content was confirmed correct\". A successful put does not mean the content is verified (content_verified: false).",
          "You want to treat a successful save as approval or a decision. No success is approval.",
          "You want a store you can use right away. No actual backend is included, and which object store to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), or that violate the side effect conditions are not sent.",
          "It checks the pinned backend revision. Requests to a backend revision (backend_profile_revision) that is not pinned are not sent.",
          "It checks the content kind and size. A content-type that does not match the magic bytes (spoofed kind) or a size over the limit is not sent.",
          "It checks for duplicate saves and overwrites. The same object_ref with the same digest is not stored twice (ALREADY_STORED), and overwrites of WORM targets are not sent.",
          "It computes the digest of content read by get itself, and if it does not match the expected value, the content is not handed over (GET_REJECTED). It rejects tampering at read time.",
          "It matches the receipt (ObjectReceipt). A receipt with a spoofed request_digest or a partial write (MISMATCH) is not accepted, and it stops from then on (HALTED).",
          "A backend failure (a 500 response, or a failure after writing) is treated as UNKNOWN and not as stored. Resending is resolved by a lookup.",
          "It checks how secrets are passed. SecretRef accepts only secret-ref/v2 locations, and a raw secret placed next to a SecretRef or the HUMAN_CREDENTIAL class is rejected."
        ],
        "what_it_does_not_do": [
          "Deciding whether content is corrupted is the role of the Kernel (integrity-facts). This Tsugite only checks before sending and after receiving.",
          "A successful put does not mean the content is verified (content_verified: false), and no success is approval (approval: false).",
          "It does not include an actual backend. Which object store to connect to, the port implementation and what to pin (endpoint and receipt format) are undecided and will be settled at a separate gate when connecting.",
          "It does not include delete or lifecycle extensions for retention/TTL (C-08) or archiving (C-09).",
          "Content kind can be confirmed by magic bytes only for application/pdf, image/png, image/jpeg, text/plain and application/json.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use an object store."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/object-store/v1. Input: pass an ObjectPutRequest and its bytes to put(ObjectPutRequest, bytes), or an ObjectGetRequest to get(ObjectGetRequest), on createObjectStoreClient({ cfg, port }). Returns: STORED, GET_VERIFIED, GET_REJECTED, REFUSED, UNKNOWN, HALTED and others. Every result has approval: false. User provides: a port to the object backend (put and get, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for WORM, EXTERNAL_REVERSIBLE for OVERWRITE, and NONE for get.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-02",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Storage integrity verification — Storage Integrity Tsugite",
        "summary": "The Storage Integrity Tsugite (継手, tsugite) confirms that an object read back from storage has not been corrupted, by comparing the record made at write time (recorded) with the observation at read-back (observed). If any one of hash, size or hash algorithm does not match, it does not trust it (fail-closed). Hash computation and reading from and writing to storage are done by the caller.",
        "problem": "It prevents stored objects from silently becoming corrupted (silent bit rot), and prevents missing tampering or substitution and using the object as is. A matching size alone is not treated as matching content; a matching hash is required separately.",
        "use_when": [
          "You want to confirm that an object read back from storage is the same as when it was written (whether it is corrupted, by checksum or ETag).",
          "You want to confirm integrity as a library on the caller's side, without placing a network relay (proxy).",
          "You want to accept only adopted hash algorithms and not trust results from any other algorithm.",
          "You have already computed or obtained the hash and size on your side and want only the comparison done."
        ],
        "do_not_use_when": [
          "You want this Tsugite to compute hashes, or to write to and read back from storage. These are not included; the caller does them and passes the results.",
          "You want to check an object for which no record (recorded) was kept. Without the hash, size and algo from write time, it cannot compare and does not trust it.",
          "You want to use it right away matched to a specific storage API's ETag format. The actual storage API and ETag format are decided in config at deployment.",
          "You want to check with a hash other than SHA-256. The verified scope is SHA-256 content hashing.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It compares hashes. If the recorded and observed hashes do not match (bit rot or tampering), it does not trust the object (ok: false).",
          "It compares sizes. If the recorded and observed sizes do not match, it does not trust the object.",
          "Even when the size is the same, it does not trust the object if the content hash differs (a size match alone is not let through).",
          "It checks the hash algorithm. Different algorithms between record and observation, and algorithms that have not been adopted, are not trusted.",
          "It checks for missing values. If the hash or size is missing, it does not trust the object."
        ],
        "what_it_does_not_do": [
          "It does not compute hashes. It does not write to or read back from storage either. The caller does these and passes the results (recorded and observed).",
          "It has no port to a backend. This Tsugite is only a pure function that compares the values it is given.",
          "If require_size is set to false, it no longer requires sizes to match (the default is true).",
          "The actual storage API and ETag format have not been decided. They are decided in config at deployment.",
          "Consolidating integrity facts as a Kernel and passing them on to signing targets or provenance is done on the Kernel side (integrity-facts, provenance-facts).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a storage API."
        ],
        "integration": "Input: call verifyIntegrity(cfg, recorded, observed) in src/guards.js (a pure function). Pass as recorded the { hash, size, algo } recorded at write time, and as observed the { hash, size, algo } observed at read-back. cfg holds allowed_algos (the adopted algorithms) and require_size (default true). Returns: { ok: true, errors: [] } on a match, otherwise { ok: false, errors: [...] }. User provides: hash computation, and reading from and writing to storage (there is no port). Runtime: Node.js, with no dependency packages.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-03",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Transactional write — Transactional Write Tsugite",
        "summary": "The Transactional Write Tsugite (継手, tsugite) checks the pinned backend, the allowed logical target and each operation's payload digest before sending an all-or-nothing (ALL_OR_NOTHING) transaction to the DB, and matches the receipt the DB returns (TransactionalWriteReceipt). A partial commit is treated as abnormal and stops it, and ABORTED is accepted only when it can be matched as having no external effect (fail-closed).",
        "problem": "It prevents assuming data \"should have been saved\" in the DB, that is, treating a half-written transaction as a success. COMMITTED is accepted only when it can be matched that all operations went in, and ABORTED only when it can be matched that nothing went in.",
        "use_when": [
          "You want to write multiple writes to a DB while keeping consistency (either all go in or none do).",
          "You do not want to trust the DB's receipt as is, and want to match it against the request (request_id, request_digest) and target before treating the write as done.",
          "You want to limit write destinations to the pinned backend and allowed logical targets, and do not want file system paths used as targets.",
          "You do not want the same request committed twice. When resending before a receipt is settled, it is resolved by a lookup.",
          "You want writing to stop until a person checks it when a partial commit or a spoofed receipt appears."
        ],
        "do_not_use_when": [
          "You need the functions of C-05 to C-09, which belong to the same contract storage-persistence/v1. They are not included in this Tsugite.",
          "You want to treat a successful write as approval or a decision. A write succeeding is not approval.",
          "You want a DB you can use right away. No actual DB is included, and which backend to connect to has not been decided.",
          "You want to write to file system paths (a leading / or \\, or ..). They are not used as targets.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the destination. Requests to a backend that is not pinned, a target that is not allowed, or a file system path target are not sent.",
          "It checks operations and payloads. Tampered payloads (digest mismatch), missing or extra payloads, duplicate operations, and operation counts or payload sizes over the limit are not sent.",
          "It does not accept partial commits. COMMITTED must cover all operations and ABORTED must cover none with no external effect; otherwise the receipt is not accepted and it stops from then on (HALTED).",
          "It checks the side effect. A receipt may only reduce the side effect declared in the request; a receipt that increases it is not accepted. COMMITTED must match the intended side effect.",
          "It matches the receipt (TransactionalWriteReceipt). A receipt with a spoofed request_digest or for another target is not accepted, and it stops from then on (HALTED).",
          "A DB failure is treated as UNKNOWN and not as written. The same request_id is not committed twice, and resending is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "Deciding consistency is the role of the Kernels (reconcile-facts, policy-facts). This Tsugite only checks before sending and after receiving.",
          "A write succeeding is not approval (approval: false).",
          "It does not include an actual DB. Which backend to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It does not include C-05 to C-09 of the same contract storage-persistence/v1. In the draft priority order, they are either not given a Tsugite (decision-only) or deferred (DEFER).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a DB client."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass a TransactionalWriteRequest and payloads (as { op_ref: bytes }) to write(TransactionalWriteRequest, payloads) on createTxClient({ cfg, port }). Returns: COMMITTED, ABORTED, REFUSED, UNKNOWN, HALTED and others. Every result has approval: false. User provides: a port to the DB (commit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE or EXTERNAL_IRREVERSIBLE, as declared in the request (NONE or LOCAL_REVERSIBLE for ABORTED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-04",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Append-only ledger — Audit Ledger Tsugite",
        "summary": "The Audit Ledger Tsugite (継手, tsugite) stacks an immutable event history on an append-only backend. Before sending, it checks the pinned ledger, the sequence and whether the event is a duplicate append, and it advances the head of the ledger only when the returned receipt (AuditAppendReceipt) matches. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When events are stacked on an external append-only store, it prevents the local head of the ledger from drifting from what the store actually holds (a sequence that rolls back or skips, duplicate appends of the same event, writes whose outcome is unknown after a failure). An append whose receipt cannot be matched never advances the head, so the way the ledger continues stays trustworthy.",
        "use_when": [
          "You want to stack an immutable event history, one event at a time, on an external append-only backend.",
          "You want to append only to one fixed ledger (pinned by ledger_id, ledger_profile_revision and contract_revision).",
          "You want to keep a single line of continuation in which the sequence never rolls back or skips.",
          "You do not want the same event to be appended twice. Duplicates are told apart by event_id and the envelope's digest; when resending before a receipt is settled, the append is resolved by a lookup.",
          "You want to append while checking locally the head of the ledger, any unresolved send and whether it has halted (state() returns { head, pending, halted })."
        ],
        "do_not_use_when": [
          "You want a store that allows rewriting or deletion. This Tsugite handles appends only.",
          "You want to stack the body of the events itself in the ledger. The envelope carries only the contract's fields, and a body mixed into it is rejected.",
          "You need a transparency log (Merkle inclusion proofs). That is outside the scope of this Tsugite.",
          "You want a store you can use right away. No actual backend is included, and which append-only service to connect to has not been decided.",
          "You want to treat a successful append as approval or a decision. A successful append is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the pinned ledger. Appends to a ledger that is not pinned are not sent.",
          "It checks the sequence. If, seen from the head of the ledger, the sequence rolls back or skips, it does not send and does not accept.",
          "It stops duplicate appends. If the same event_id with the same content is already appended, it does not send to the backend and returns ALREADY_APPENDED; if the same event_id comes with different content, it returns REFUSED.",
          "While a send is unresolved, it does not send the next envelope and returns REFUSED. A send whose outcome was unknown is resolved by a lookup.",
          "A backend failure (a 500 response or a partial write) is treated as UNKNOWN, and the head of the ledger is not advanced.",
          "If the ledger, sequence, event_id or target in the receipt (AuditAppendReceipt) differs from the envelope that was sent, it does not accept the receipt and does not advance the head of the ledger.",
          "If a receipt is inconsistent, it stops (HALTED) and does not append until a person checks it.",
          "It rejects envelopes that contain a body, events over the envelope size limit (max_envelope_bytes), and envelopes with a spoofed kind (content-type spoofing)."
        ],
        "what_it_does_not_do": [
          "Deciding whether the ledger can be altered is the role of the Kernels (provenance-facts and integrity-facts). This Tsugite only checks before sending and matches the receipt.",
          "A successful append is not approval (approval: false).",
          "It does not include an actual backend. Which append-only service to connect to, the port implementation and what to pin (endpoint and receipt format) are undecided and will be settled at a separate gate when connecting.",
          "It does not handle transparency logs (Merkle inclusion proofs).",
          "It does not resolve the secret (SecretRef) used for backend authentication. That is the responsibility of the port.",
          "It is not a part dedicated to C-04. The same single part serves H-01 (append-only audit log), H-08 (tamper-detection chain) and H-02 (access log), and the tests and inspection are shared.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-ledger/v1. Input: pass an AuditEventEnvelope to append(envelope) on createLedgerClient({ cfg, port }); cfg holds the pinned ledger and the envelope size limit. Returns: one of APPENDED_VERIFIED, ALREADY_APPENDED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. state() returns { head, pending, halted }. User provides: a port to the append-only backend (append, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE (an external write that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-05",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Idempotent write (WAL) — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks idempotent write (IdempotentWrite) requests to a storage backend against the pinned backend, the allowed logical targets and the payload limit before sending them, and checks the backend's receipt against the contract. The external effect happens at most once; if anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "It prevents writing the same content twice when a write to storage is resent, writing different content under the same idempotency key, and treating a write as done without checking the backend's receipt.",
        "use_when": [
          "You want a write to the storage backend to have its external effect at most once, even when it is resent.",
          "You want to accept a no-effect duplicate result (DEDUP_HIT) only after checking that the stored content is the same as the content you sent.",
          "You want to check the payload's digest yourself and not send a payload over the size limit.",
          "When the result is unknown because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You need a transactional write. The C-03 transactional write is a separate part (transactional-write-tsugite).",
          "You want this Tsugite to decide whether something is a duplicate. Duplicate detection is the role of the Kernel (reconcile-facts).",
          "You want to write to a location on a file system. Targets are logical targets only; file-system targets are rejected.",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to treat a completed write as approval or a decision. A completed write is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "A request that writes different content under the same idempotency key is not sent (REFUSED).",
          "It checks the payload. A payload over the size limit, or one that does not match its digest (tampered), is not sent.",
          "A duplicate (DEDUP_HIT) receipt that carries a new effect is not accepted. DEDUP_HIT must have no effect (NONE).",
          "If the stored content (stored_digest) differs from the content sent, the receipt is not accepted, even for DEDUP_HIT. Different content under the same key is treated as an anomaly.",
          "It checks the receipt. A spoofed request_digest, a receipt for another target, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "A backend failure is treated as UNKNOWN, and the same request_id is never sent twice. While a submission is unresolved, it returns REFUSED and does not send to the backend.",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE)."
        ],
        "what_it_does_not_do": [
          "Deciding whether something is a duplicate is the role of the Kernel (reconcile-facts). This Tsugite only checks before sending and matches after receiving.",
          "A completed write is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a separate part for this item alone. Under the contract, C-05 to C-09, H-06 and F-04 are one part (tsugite/storage-persistence/lifecycle); this item is its idempotent-write facet, and F-04 (idempotency key handling) is also served by this facet.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a storage service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass an IdempotentWrite request and the bytes to write (required) to submit(request, bytes) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries dedup_result. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup) and the backend, target and limit settings. Side effect: EXTERNAL_* (NONE for DEDUP_HIT).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-06",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Backup and snapshot — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks snapshot (Snapshot) requests to a storage backend against the pinned backend and the allowed logical targets before sending them, and checks the backend's receipt against the contract. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When a snapshot request is sent, it prevents sending a target that is not allowed, and prevents treating a snapshot as taken without checking that the backend's receipt really belongs to that target. Only snapshots this Tsugite has matched can be the source of a simulated restore (C-07).",
        "use_when": [
          "You want to send a snapshot request for a logical target to the storage backend, and accept that the snapshot was taken as a fact only after the receipt is matched.",
          "You want to match a snapshot receipt together with the digest of that snapshot_ref's content (content_digest).",
          "You want only matched snapshots to be treated as the source of a simulated (SYNTHETIC) or dry-run restore.",
          "When the result is unknown because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You need an actual restore from a snapshot (production PITR). A destructive real restore is out of scope; restores are simulated or dry-run only.",
          "You want to copy a location on a file system. Targets are logical targets only; file-system targets are rejected.",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to treat a taken snapshot as approval or a decision. A taken snapshot is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "It checks the receipt. A spoofed request_digest, a receipt for another target, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "The effect of a snapshot receipt may be at most a reversible external effect (EXTERNAL_REVERSIBLE). Receipts that increase the effect beyond that are not accepted.",
          "It keeps matched snapshots in the snapshots of state(), and does not send a restore request from a snapshot that has not been matched.",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE).",
          "A backend failure is treated as UNKNOWN, and the same request_id is never sent twice. While a submission is unresolved, it returns REFUSED and does not send to the backend."
        ],
        "what_it_does_not_do": [
          "A matched snapshot does not confirm that you can actually restore from it. A destructive real restore (production PITR) is out of scope.",
          "Deciding what to snapshot and when is the role of the Kernel. This Tsugite only checks before sending and matches after receiving.",
          "A taken snapshot is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a separate part for this item alone. Under the contract, C-05 to C-09, H-06 and F-04 are one part (tsugite/storage-persistence/lifecycle); this item is its snapshot facet.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a storage service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass a Snapshot request to submit(request) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries snapshot_ref and snapshot_state. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup) and settings for the pinned backend and the allowed targets. Side effect: EXTERNAL_REVERSIBLE (an external effect that can be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-07",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Recovery and PITR (simulated and dry-run only) — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks restore (Restore) requests, limited to simulated (SYNTHETIC) and dry-run restores, before sending them, and checks the backend's receipt against the contract. It does not perform a destructive real restore (production PITR) and does not send such a request. Only snapshots this Tsugite has matched can be the source (fail-closed).",
        "problem": "When a restore request is sent, it prevents restoring from a snapshot that has not been matched, trusting a result that reports a dry-run as applied, and sending a request for a destructive real restore. This Tsugite handles only simulated and dry-run restores; it does not actually return data to a point in time.",
        "use_when": [
          "You want to send a simulated (SYNTHETIC) or dry-run restore request based on a matched snapshot, and match its receipt.",
          "You want to accept only a no-effect (NONE) receipt as the result of a dry-run restore.",
          "You want to limit the source of a simulated or dry-run restore to snapshots this Tsugite has matched as CREATED."
        ],
        "do_not_use_when": [
          "You want to actually return to a point in time (production PITR). A destructive real restore is out of scope, and the contract defines only simulated and dry-run restores. This Tsugite does not send such a request.",
          "You want a means of actually recovering after an incident. This Tsugite does not perform actual recovery.",
          "You want a restore from a snapshot this Tsugite has not matched.",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It does not send requests in a destructive real-restore mode.",
          "It does not send a restore request from a snapshot this Tsugite has not matched as CREATED.",
          "It does not accept a receipt that reports a dry-run request as applied. APPLIED_SYNTHETIC is accepted only for a simulated (SYNTHETIC) request.",
          "The effect of a restore receipt may be at most no effect (NONE) or a locally reversible effect (LOCAL_REVERSIBLE), and only NONE for dry-run. Receipts that increase the effect are not accepted.",
          "A spoofed request_digest or a receipt for another target is not accepted, and it stops from then on (HALTED).",
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE).",
          "A backend failure is treated as UNKNOWN, and the same request_id is never sent twice. While a submission is unresolved, it returns REFUSED and does not send to the backend."
        ],
        "what_it_does_not_do": [
          "It does not perform actual recovery. A destructive real restore (production PITR) is out of scope, and the contract defines only simulated and dry-run restores. The restore_state attached to DONE is the result of a simulated or dry-run restore.",
          "The fuzzing does not call a backend, and the tests use a stand-in for the storage backend. Returning an actual backend to a point in time has not been verified.",
          "Deciding what to restore and when is the role of the Kernel. This Tsugite only checks before sending and matches after receiving.",
          "A matched simulated restore is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a separate part for this item alone. Under the contract, C-05 to C-09, H-06 and F-04 are one part (tsugite/storage-persistence/lifecycle); this item is its simulated-restore facet.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass a Restore request (simulated or dry-run) to submit(request) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries restore_state. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup). Side effect: NONE or LOCAL_REVERSIBLE (NONE for dry-run); actual recovery is not included.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-08",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Retention and TTL — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks retention (Retention) requests (EVALUATE, RETAIN, EXPIRE) against the pinned retention policies and logical targets before sending them, and checks the receipt against the contract. Irreversible deletion (EXPIRE) is permitted by the allow_expire setting (default false) and is sent only for targets without a legal hold (fail-closed).",
        "problem": "When deletions based on retention periods are sent to the backend, it prevents sending deletions that are not permitted or deletions of targets under legal hold, treating an expiry evaluation alone as a deletion, and treating data as deleted without checking the backend's receipt.",
        "use_when": [
          "You want to send deletion (EXPIRE) requests that follow a retention policy to the backend only when the setting permits it.",
          "You want to receive the fact of a legal hold (a Kernel or human decision) and stop sending EXPIRE for that target from then on.",
          "You want to ask the backend only for an evaluation of whether data has expired (EVALUATE), without deleting.",
          "You want to treat data as deleted (deleted: true) only when the EXPIRE receipt is matched as EXPIRED."
        ],
        "do_not_use_when": [
          "You want this Tsugite to decide retention periods or legal holds. Those decisions belong to Kernels and people; this Tsugite only receives the facts and enforces them.",
          "You want the mechanism that deletes data automatically when it expires. This Tsugite only checks and sends the retention requests it is given, and it does not send EXPIRE unless the allow_expire setting is true.",
          "You want to move data to another tier on expiry. Moving between tiers is an Archive request, the archive and tiering facet (C-09).",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "When the allow_expire setting is not true, it does not send EXPIRE.",
          "It does not send EXPIRE for a target under legal hold.",
          "An EVALUATE receipt that involves deletion is not accepted. EXPIRED for EVALUATE is only an expiry evaluation, not a deletion.",
          "A receipt that returns EXPIRED for RETAIN is treated as an anomaly and not accepted.",
          "Requests for a retention policy that is not in the pinned list are not sent.",
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "It checks the receipt. A spoofed request_digest, a receipt for another target, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE). A backend failure is treated as UNKNOWN, and the same request_id is never sent twice."
        ],
        "what_it_does_not_do": [
          "It does not decide retention periods or legal holds. That is the role of the Kernels (redact-facts and policy-facts) and people; this Tsugite only receives those facts and enforces them.",
          "A completed deletion is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a separate part for this item alone. Under the contract, C-05 to C-09, H-06 and F-04 are one part (tsugite/storage-persistence/lifecycle); this item is its retention facet, and H-06 (retention enforcement) is also served by this facet.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a storage service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass a Retention request to submit(request) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries decision, deleted and effect. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup) and settings for the pinned retention policies (retention_policies) and allow_expire. Side effect: EXTERNAL_IRREVERSIBLE for EXPIRE, NONE otherwise.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-09",
        "category": "C/保管・永続化",
        "kind": "Tsugite",
        "title": "Archive and tiering — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks tier-transition (Archive) requests to a storage backend against the pinned tiers and the allowed logical targets before sending them, and checks the backend's receipt against the contract. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When a request to move data to another tier is sent, it prevents sending to a tier that is not pinned or a target that is not allowed, and prevents treating data as moved without checking the backend's receipt.",
        "use_when": [
          "You want to send the backend a request to move old data to a pinned tier.",
          "You want to accept the result of the move (transition_state and archive_ref) as a fact only after the receipt is matched.",
          "When the result is unknown because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want this Tsugite to decide which data to move and when. That decision is the role of the Kernel; this Tsugite only checks before sending and after receiving.",
          "You want the move to include irreversible deletion. An archive's effect is at most a reversible external effect (EXTERNAL_REVERSIBLE); irreversible deletion is handled by EXPIRE in retention (C-08).",
          "You want to move data to a location on a file system. Targets are logical targets only; file-system targets are rejected.",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Requests to move data to a tier that is not pinned are not sent.",
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "The effect of an archive receipt may be at most a reversible external effect (EXTERNAL_REVERSIBLE). Receipts that increase the effect beyond that are not accepted.",
          "A spoofed request_digest or a receipt for another target is not accepted, and it stops from then on (HALTED).",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE).",
          "A backend failure is treated as UNKNOWN, and the same request_id is never sent twice. While a submission is unresolved, it returns REFUSED and does not send to the backend."
        ],
        "what_it_does_not_do": [
          "Deciding what to move and when is the role of the Kernel. This Tsugite only checks before sending and matches after receiving.",
          "A completed move is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a separate part for this item alone. Under the contract, C-05 to C-09, H-06 and F-04 are one part (tsugite/storage-persistence/lifecycle); this item is its tier-transition facet.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a storage service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass an Archive request to submit(request) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries transition_state and archive_ref. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup) and settings for the pinned backend, the tiers (tiers) and the allowed targets. Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-10",
        "category": "C/保管・永続化",
        "kind": "Component",
        "title": "Raw data storage (append-only) — Raw storage (append-only, content-addressed, partitioned) part (Component)",
        "summary": "The Raw storage part (Component) is a part that places fetched raw data, without fitting it to a schema, append-only into a separate storage target for each retention_class (pii, business, evidence). It builds fetch.json with the fetch record part (Primitive) (N-09), checks that the bytes passed in match the record's name, byte_size and sha256, and writes the payload in the record's order, and fetch.json last, with the idempotent write Tsugite (C-05). The idempotency key is the path. read reads with the C-01 Tsugite, attaching the expected sha256, and returns the bytes only when the digest matches. There are no delete or overwrite operations.",
        "problem": "Behind \"it has been saved\", no one checks whether the data remains as it was. This part prevents placing bytes that do not match the record, overwriting a key once placed with different content, mixing PII with other partitions, and using bytes that were read even though they differ from what was placed.",
        "use_when": [
          "You want to place the fetched files (1 to 10000) and fetch.json append-only, once, under the keys raw/<source>/<folder_stamp>/payload/<name> and raw/<source>/<folder_stamp>/fetch.json (store).",
          "You want a separate storage target for each retention class, with a backend and a client for the PII partition that are separate from the other partitions. Every write goes only to the target of the partition for that fetch's retention_class.",
          "You want a PARTIAL fetch to keep what was fetched, the same as any other. The rest comes with the next fetch, in a different folder with a different fetched_at.",
          "You want to read stored raw data with the expected sha256 attached, have the part recompute the sha256 itself after the Tsugite's check, and receive a copy of the bytes only when it matches (read).",
          "You want to resend a write whose result is unknown as the same request and continue with the remaining writes (resolvePending). Storing the same fetch again continues from where it stopped, with the already written parts reported as DEDUP_HIT."
        ],
        "do_not_use_when": [
          "You want it to also count retention periods, delete on expiry, or enforce the rules on PII retention periods. This part only places data in separate partitions. Retention and deletion are on the side of the backend's policy (C-08 and others).",
          "You want to check that the fetched content is the same as the content on the platform. It only checks that the bytes passed in match the name, byte_size and sha256 in the N-09 record.",
          "You want to check that the PII partition is really a separate storage. It checks only that the backend_profile_ref strings and the client objects differ; it cannot tell when two names point to the same thing.",
          "You want this part to enforce a file size limit. The limit is set by max_payload_bytes of the C-05 Tsugite. Each send of a large file makes a copy of it.",
          "You want this part to detect, with RAW_CONFLICT, keys written by other processes or before a restart. Its record of keys is only inside the process; such keys are refused by the Tsugite and the backend (neither overwrites).",
          "You want two instances with the same request_prefix to use the same client. The Tsugite answers the same request_id with ALREADY_DONE, and a different request comes back with the earlier answer. Make request_prefix unique per client.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, there are not exactly 3 partitions, two partitions share the same storage target, pii shares a backend or client with another partition, or a port function is missing, it stops with INVALID_OPTIONS.",
          "When the input to an operation is malformed (missing or extra fields, files is not a plain array of 1 to 10000 items, duplicate names, bytes is not a Uint8Array, the read key does not start with raw/, and so on), it stops with INVALID_INPUT, and when a getter throws an exception, with INSPECTION_FAILED.",
          "When N-09 does not build a record, it stops with FETCH_RECORD_REFUSED (with the N-09 errors attached); when N-09 throws an exception, with FETCH_RECORD_PORT_FAILED; and when the answer is inconsistent (the sha256 of canonical, the canonical form, schema, input fields, folder_stamp, the shape of files), with FETCH_RECORD_INVALID; and it sends nothing.",
          "When the files passed in do not match the files in the record, it sends nothing (FILE_MISMATCH). errors[0] carries name and reason (MISSING, SIZE, DIGEST, EXTRA).",
          "When this part has previously sent a different sha256 for a key in the write plan, it sends nothing (RAW_CONFLICT) (append-only; no overwrite).",
          "When the result of a write is unknown, it keeps the same request with STORE_UNKNOWN and stops store with STORE_PENDING while that is pending. When the Tsugite did not send, it reports WRITE_REFUSED, and when the Tsugite halted, the receipt could not be matched, or the result is not APPLIED or DEDUP_HIT, WRITE_FAILED; and it does not move on.",
          "In read, it reports OBJECT_NOT_FOUND when the object does not exist, DIGEST_MISMATCH when the digest does not match (GET_REJECTED from the Tsugite, or a different sha256 when recomputed by the part itself), READ_UNKNOWN when the result is unknown, READ_REFUSED when the Tsugite did not send, and READ_FAILED on a halt or a malformed result; and it does not return the bytes.",
          "When timestamp does not return an existing date-time in RFC 3339 UTC (Z) or throws, it sends nothing (CLOCK_FAILED), and when the previous operation has not finished, it stops with BUSY."
        ],
        "what_it_does_not_do": [
          "The Raw storage part is not a Tsugite. It has no Request or Receipt of its own; the external effects of writing and reading are caused by the bundled C-05 and C-01 Tsugite under their own contracts. This part itself does not touch the outside.",
          "The meaning of the bundled parts (recorded ≠ the fetch is correct, written ≠ accepted, read ≠ the content is correct) stays on each part's side.",
          "There are no delete or overwrite operations. It sends only IdempotentWriteRequest and ObjectGetRequest. It does not count retention periods and does not delete.",
          "Being written or read is not proof that the content is correct, and it is neither acceptance nor approval (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "Whether the C-05 key and the C-01 object_ref point to the same stored object (whether raw data that was written can be read with read) is the job of the backend profile. The tests checked this with stand-ins that point to the same stored object.",
          "In this part's tests it is not connected to the real N-09, C-05 or C-01. The unit tests and fuzzing use stand-ins, and the wiring with the real ones is checked by a separate wiring check. The state (the record of sent keys, the pending write) is only inside the process and is lost on restart.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular data lake or object storage."
        ],
        "integration": "Create it with createRawStore(options) in src/raw-store.js. It takes partitions { pii, business, evidence }, request_prefix, ports and an optional timestamp, and returns { ok, errors, instance }. The operations are store, read, resolvePending and status, and every result carries approval: false. The ports the user provides are fetchRecord (N-09) and, for each partition, write.<class>.submit (the client of the C-05 Tsugite) and read.<class>.get (the client of the C-01 Tsugite). Side effects are caused by the bundled Tsugite (EXTERNAL_REVERSIBLE), and credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "C-11",
        "category": "C/保管・永続化",
        "kind": "Component",
        "title": "Generational backup per retention class — Per-partition generational backup and restore check part (Component)",
        "summary": "The Per-partition generational backup and restore check part (Component) is a part that takes backups for each retention class (pii, business, evidence) with a separate storage target, a separate client and a separate generation count. backup has the snapshot Tsugite (C-06) create a snapshot, and records only snapshots that could be matched as generation records with the idempotent write Tsugite (C-05). verifyRestore sends only simulated restores (C-07, DRY_RUN or SYNTHETIC), and marks a generation as \"checked as restorable\" only when the digest of the restored content matches the generation's content_digest through the C-02 Tsugite.",
        "problem": "A backup is not tested until it is restored. This part prevents turning a snapshot that cannot be matched into a generation, mixing PII with other partitions, overwriting a generation, and treating an unchecked backup as \"restorable\".",
        "use_when": [
          "You want to have a snapshot created for each partition and record a snapshot that could be matched, once, as the next generation at backup/<class>/<seq>.json (backup). Generation numbers start at last_seq + 1 and count only generations that were written.",
          "You want to send a SYNTHETIC simulated restore from a recorded generation and match the digest of the restored content returned by readRestored against the generation's content_digest (verifyRestore).",
          "You want to receive the fact that the generations have exceeded keep_generations as over_limit. Old generations are not deleted.",
          "You want to list, for each partition, the generations, the check results (restore_verified) and what is pending (status).",
          "You want a send whose result is unknown to be held as pending for that partition only, and resent as the same request (resolvePending). While it is pending, the other partitions do not stop."
        ],
        "do_not_use_when": [
          "You want to restore to production (a restore that actually overwrites). It sends only DRY_RUN or SYNTHETIC, and whether a restore works in production has not been checked.",
          "You want old generations deleted on expiry. keep_generations only returns the fact over_limit; deleting is the job of the backend's retention (C-08 and others).",
          "You want to treat a backup just taken as \"restorable\". Until the digest match in verifyRestore (SYNTHETIC, with readRestored) is done, restore_verified is false. When there is no readRestored, or with DRY_RUN, digest_verified is null.",
          "You want it to also check that a snapshot's content_digest correctly represents the content, or that readRestored really computed the digest from the restored content. This part only matches the returned values against each other.",
          "You want to run verifyRestore directly on generations taken before a restart. The state is only inside the process; after a restart, the count is restarted by passing last_seq, and generations from the earlier process are not targets of verifyRestore.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, there are not exactly 3 partitions, two partitions share the same storage target, keep_generations or last_seq is malformed, or a port function is missing, it stops with INVALID_OPTIONS; when the input to an operation is malformed, with INVALID_INPUT; and when a getter throws an exception, with INSPECTION_FAILED.",
          "When the result of a snapshot is unknown, it reports SNAPSHOT_UNKNOWN (keeping the same request); when the Tsugite did not send, SNAPSHOT_REFUSED; and when the state is not CREATED, the Tsugite halted, or the result or content_digest is malformed, SNAPSHOT_FAILED; and it does not make it a generation.",
          "When the result of writing a generation record is unknown, it reports RECORD_UNKNOWN (keeping the same request); when the Tsugite did not send (including different content for the same key), RECORD_REFUSED; and when the Tsugite halted or the result is not APPLIED or DEDUP_HIT, RECORD_FAILED; and it does not count it as a generation.",
          "In verifyRestore, when the snapshot_ref is not a generation recorded by this part, it stops with UNKNOWN_GENERATION.",
          "When the result of a restore is unknown, it stops with RESTORE_UNKNOWN; when the Tsugite did not send or the backend refused the restore, with RESTORE_REFUSED; and when the Tsugite halted, the restore_state is unknown, or it claimed to have applied a DRY_RUN, with RESTORE_FAILED.",
          "When the restored digest does not match the generation's content_digest (C-02 does not return ok: true, it is not sha256, or the value differs), it reports RESTORE_DIGEST_MISMATCH (digest_verified: false); when readRestored throws or returns a malformed value, RESTORED_READ_FAILED; and when the C-02 function throws, INTEGRITY_PORT_FAILED (both with digest_verified: null); and it does not count it as checked.",
          "When that partition has a send whose result is unknown, it stops with CLASS_PENDING; calling resolvePending when nothing is pending returns NOTHING_PENDING; when timestamp does not return an existing date-time, it stops with CLOCK_FAILED; and when the previous operation has not finished, with BUSY."
        ],
        "what_it_does_not_do": [
          "The Per-partition generational backup and restore check part is not a Tsugite. It has no Request or Receipt of its own; the external effects of snapshots, simulated restores and writing generation records are caused by the bundled Tsugite (C-05, C-06, C-07) under their own contracts. This part itself does not touch the outside.",
          "The meaning of the bundled parts (created ≠ the content is correct, simulated restore ≠ production restore, matched ≠ approval) stays on each part's side.",
          "It does not send delete, retention or archive requests, and does not delete old generations. Generations are not overwritten.",
          "The results are not approval (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "When a generation record was stored but the result was malformed, the key for that seq remains in the backend and the record of the next backup keeps being refused (fail-closed). Fixing it is the caller's job, by aligning last_seq.",
          "In this part's tests it is not connected to the real storage-lifecycle or storage-integrity Tsugite or to a backend. The unit tests and fuzzing use stand-ins, and the wiring with the real ones is checked by a separate wiring check.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular backup product."
        ],
        "integration": "Create it with createBackup(options) in src/backup.js; it returns { ok, errors, instance }. options are policies { pii, business, evidence } (target, keep_generations and an optional last_seq), request_prefix, ports and an optional timestamp. The operations are backup, verifyRestore, resolvePending and status, and the results carry approval: false. Ports: storage.<class>.submit for each partition (the client of the storage-lifecycle Tsugite), verifyIntegrity (C-02) and an optional readRestored. Credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-01",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Email delivery (transactional) — Email Delivery Tsugite",
        "summary": "The Email Delivery Tsugite (継手, tsugite) sends confirmation, payment-receipt and reminder mail to a mail backend only after checking the pinned recipient, verified consent, redaction record, body digest, headers free of recipient spoofing and idempotency key, then matches the backend's receipt (NotificationReceipt) against the contract. If anything does not match, it does not send (fail-closed).",
        "problem": "It prevents email from ending at \"it was delivered\": sending mail with a spoofed recipient, mail without consent or garbled mail, and mistaking \"it was sent\" for \"it arrived\". The sent state (REQUESTED) is not delivery and is treated as delivered: false.",
        "use_when": [
          "You want to send emails such as confirmations, payment receipts and reminders through an external mail backend.",
          "You want to limit recipients to a pinned recipient table (recipient_ref and email address).",
          "You do not want to send to recipients without verified consent, or to recipients with a recorded bounce or complaint.",
          "You do not want the same email to be sent twice. When resending before a receipt is settled, the send is resolved by a lookup.",
          "You want sending to stop until a person checks it when a spoofed receipt appears."
        ],
        "do_not_use_when": [
          "You want to send by SMS, push or chat. This Tsugite handles email only; other channels are out of scope.",
          "You want this Tsugite to confirm delivery (D-05) or judge bounces and complaints (D-06). Those belong to the Kernel side; this Tsugite only receives their results.",
          "You want to send to several people with Cc or Bcc. There must be exactly one To that matches the pinned recipient, and Cc, Bcc and Resent-* are not sent.",
          "You want to treat a successful send as delivery or approval. Being sent is not delivery, and delivery is not approval. Consent is not Authority either.",
          "You want a mail backend you can use right away. No actual mail backend (SMTP or a provider's API) is included, and which one to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks for recipient spoofing. Mail whose To does not match the pinned recipient, that has two To addresses, that has Cc or Bcc, or whose headers carry CR/LF injection is not sent. Nothing is sent to a recipient that is not pinned.",
          "It checks consent. Sends without consent, or with consent that has not been verified, are not sent.",
          "It does not send to recipients with a recorded bounce or complaint.",
          "It checks the body. A body that does not match its digest (tampering) or that exceeds the size limit is not sent.",
          "It checks for duplicate sends. The same idempotency_key is not sent again (ALREADY_SENT).",
          "It checks the receipt (NotificationReceipt). A receipt with a spoofed request_digest, delivered without evidence, UNKNOWN upgraded to sent, or a mismatched side effect is not accepted, and it stops from then on (HALTED).",
          "A backend failure is treated as UNKNOWN, not as a send. A resend is resolved by a lookup.",
          "It checks how secrets are passed. A request that places a raw secret next to a SecretRef is rejected."
        ],
        "what_it_does_not_do": [
          "Deciding recipient permission, delivery consent and body minimization is the role of the Kernels (policy-facts, consent-facts and redact-facts). This Tsugite only receives their results and checks against them.",
          "Being sent is not delivery (REQUESTED is delivered: false). No result is approval (approval: false).",
          "It does not include an actual mail backend (SMTP or a provider's API). The port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It does not handle channels other than email (SMS, push, chat).",
          "It does not confirm delivery (D-05) or judge bounces and complaints (D-06). It only receives those results.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a mail API."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1. Input: pass a NotificationRequest, the bytes and verifiedConsents to send(NotificationRequest, bytes, verifiedConsents) on createMailClient({ cfg, port }); bounces and complaints are passed with recordSuppression. Returns: one of SENT, ALREADY_SENT, FAILED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the mail backend (send, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE (an external send that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-02",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "SMS delivery — SMS and Push Delivery Tsugite",
        "summary": "The SMS and Push Delivery Tsugite (継手, tsugite) checks an SMS notification request (NotificationRequest, channel sms) against the pinned recipient table, verified consent, the redaction record, the idempotency key, the body digest and limit, and the form of an SMS body before handing it to the sending platform, and matches the returned receipt (NotificationReceipt) against the contract. It carries SMS and does not include identity verification. If any check fails, it does not send (fail-closed).",
        "problem": "When sending SMS, it prevents sending to numbers that are not pinned or malformed numbers (numbers not in E.164), sending without consent or a redaction record, sending the same notification twice, and mistaking \"requested\" for \"delivered\" or \"read\".",
        "use_when": [
          "You want to send notification SMS only to numbers (E.164) in the pinned recipient table.",
          "You want to confirm before sending that there is verified consent and a record of body minimization (both can be made required in the settings).",
          "You do not want the same notification sent twice. The idempotency key is required, and when resending before a receipt is settled, the send is resolved by a lookup.",
          "You want to stop sending to recipients that had a bounce or complaint (recordSuppression).",
          "You want to handle requested, delivered, read and acknowledged separately, each by its own evidence."
        ],
        "do_not_use_when": [
          "You want to use it for identity verification. This Tsugite carries SMS notification requests and does not include identity verification. An SMS being delivered or read is not verification of the person's identity.",
          "You want to send on the email or webhook channel. The only channels are sms and push. D-01 is the mail Tsugite and D-04 is the notify Tsugite.",
          "You want to treat a successful send or delivery as approval or a sign-off. Being sent or delivered is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a sending platform you can use right away. No actual sending platform (which SMS service) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "It checks recipients. Recipients not in the pinned recipient table, suppressed recipients (after a bounce or complaint), requests whose channel does not match the recipient's channel, and requests on the email or webhook channel are not sent.",
          "It checks the form of the recipient table. An SMS recipient must be in E.164 (+ followed by 7 to 15 digits starting with the country code). If even one recipient in the table is malformed (not E.164, or containing whitespace), the whole table is not used.",
          "It checks the SMS body. A body that is not valid UTF-8, or that contains control characters other than line breaks, is not sent.",
          "It does not send a body whose digest has been tampered with, or a body over the limit (max_payload_bytes).",
          "It checks consent and minimization. When they are made required in the settings, requests without consent or with consent that has not been verified, and requests without a redaction record, are not sent.",
          "Requests without an idempotency key are not sent. The same idempotency key is not sent twice (ALREADY_SENT).",
          "For authentication to the sending platform it carries only the location of a SecretRef. A request that puts the secret itself in the SecretRef field is not sent.",
          "It matches the receipt (NotificationReceipt). A delivered, read or acknowledged state without evidence, a send without a message_id, a mismatched side effect and a spoofed request_digest are not accepted, and it stops from then on (HALTED). A sending-platform failure is treated as UNKNOWN, not as a send."
        ],
        "what_it_does_not_do": [
          "It does not verify identity. It carries SMS notification requests and does not include identity verification. Delivered or read is not verification of the person's identity.",
          "Being sent is not delivery. delivered on SENT is true only in a state that has evidence of delivery. Merely requested (REQUESTED) is not delivered.",
          "It does not look at SMS length (limits from message splitting or character encoding). The only limit is in bytes.",
          "Deciding whether consent is valid and whether minimization is sufficient is the role of the Kernels (consent-facts and redact-facts), and recipient permission is the role of policy-facts. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual sending platform. Which service to connect to, the port implementation and the recipient table are undecided and will be settled at a separate gate when connecting.",
          "Being sent or delivered is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1 (channel sms). Input: pass a NotificationRequest, the bytes and verifiedConsents to send(NotificationRequest, bytes, verifiedConsents) on createSmsPushClient({ cfg, port }). Returns: one of SENT, ALREADY_SENT, FAILED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the sending platform (send, and optionally lookup) and the recipient table recipients in cfg. Side effect: EXTERNAL_IRREVERSIBLE (an external send that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-03",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Push notifications — SMS and Push Delivery Tsugite",
        "summary": "The SMS and Push Delivery Tsugite (継手, tsugite) checks a push notification request (NotificationRequest, channel push) against the pinned recipient table (device references), verified consent, the redaction record, the idempotency key, the body digest and limit, and the form of a push body before handing it to the sending platform, and matches the returned receipt (NotificationReceipt) against the contract. It does not let the push body replace the destination, and if any check fails, it does not send (fail-closed).",
        "problem": "When sending push notifications, it prevents putting a destination (such as token, to or topic) in the body and redirecting to a device that is not pinned, sending without consent or a redaction record, sending the same notification twice, and mistaking \"requested\" for \"delivered\" or \"read\".",
        "use_when": [
          "You want to send push notifications only to device references (references without whitespace) in the pinned recipient table.",
          "You want the destination decided only by the recipient table, and do not want the body (JSON) to change it.",
          "You want to confirm before sending that there is verified consent and a record of body minimization (both can be made required in the settings).",
          "You do not want the same notification sent twice. The idempotency key is required, and when resending before a receipt is settled, the send is resolved by a lookup.",
          "You want to stop sending to recipients that had a bounce or complaint (recordSuppression).",
          "You want to handle requested, delivered, read and acknowledged separately, each by its own evidence."
        ],
        "do_not_use_when": [
          "You want to confirm the meaning of the push body's contents (whether it is a correct notification). This Tsugite only stops replacement of the destination.",
          "You want to send on the email or webhook channel. The only channels are sms and push. D-01 is the mail Tsugite and D-04 is the notify Tsugite.",
          "You want to treat a successful send or delivery as approval or a sign-off. Being sent or delivered is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a sending platform you can use right away. No actual sending platform (which push service) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "It checks recipients. Recipients not in the pinned recipient table, suppressed recipients (after a bounce or complaint), requests whose channel does not match the recipient's channel, and requests on the email or webhook channel are not sent.",
          "It checks the form of the recipient table. A push recipient is a device reference without whitespace, and if even one recipient in the table is malformed, the whole table is not used.",
          "It checks the push body. A body that is not a UTF-8 JSON object, or a body that has token, to, topic, registration_ids, condition or target (replacing the destination), is not sent.",
          "It does not send a body whose digest has been tampered with, or a body over the limit (max_payload_bytes).",
          "It checks consent and minimization. When they are made required in the settings, requests without consent or with consent that has not been verified, and requests without a redaction record, are not sent.",
          "Requests without an idempotency key are not sent. The same idempotency key is not sent twice (ALREADY_SENT).",
          "For authentication to the sending platform it carries only the location of a SecretRef. A request that puts the secret itself in the SecretRef field is not sent.",
          "It matches the receipt (NotificationReceipt). A delivered, read or acknowledged state without evidence, a send without a message_id, a mismatched side effect and a spoofed request_digest are not accepted, and it stops from then on (HALTED). A sending-platform failure is treated as UNKNOWN, not as a send."
        ],
        "what_it_does_not_do": [
          "Being sent is not delivery. delivered on SENT is true only in a state that has evidence of delivery. Merely requested (REQUESTED) is not delivered.",
          "It does not look at the meaning of the push body's contents (whether it is a correct notification). It only stops replacement of the destination.",
          "Deciding whether consent is valid and whether minimization is sufficient is the role of the Kernels (consent-facts and redact-facts), and recipient permission is the role of policy-facts. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual sending platform. Which service to connect to, the port implementation and the recipient table are undecided and will be settled at a separate gate when connecting.",
          "Being sent or delivered is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific SMS or push service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1 (channel push). Input: pass a NotificationRequest, the bytes and verifiedConsents to send(NotificationRequest, bytes, verifiedConsents) on createSmsPushClient({ cfg, port }). Returns: one of SENT, ALREADY_SENT, FAILED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the sending platform (send, and optionally lookup) and the recipient table recipients in cfg. Side effect: EXTERNAL_IRREVERSIBLE (an external send that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-04",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Webhook delivery — Notification Delivery Tsugite",
        "summary": "The Notification Delivery Tsugite (継手, tsugite) only decides, before an event notification such as a Webhook is sent outward, whether it may be sent. It checks the pinned recipient, allowed event, idempotency key, signed payload and absence of duplicates; if any is missing, it decides \"do not send\" (fail-closed). It does not send, resend or sign payloads; it only decides before sending.",
        "problem": "As the backstage plumbing of integrations that send notifications outward, it uses a pre-send decision to prevent sending to recipients that are not pinned (arbitrary URLs; SSRF), sending events that are not allowed, sending the same notification twice, and sending payloads without a signature.",
        "use_when": [
          "You want to place it in front of your own sending code so that it decides whether a notification such as a Webhook may be sent.",
          "You want to limit destinations to the set of pinned recipients and reject sending to arbitrary URLs (anti-SSRF).",
          "You want to limit the event types that may be sent to an allowed list (allowlist).",
          "You keep your own set of idempotency keys already sent and want to stop a duplicate send of the same notification before it is sent."
        ],
        "do_not_use_when": [
          "You want a part that actually sends Webhooks, resends them on failure, or signs payloads. This Tsugite does none of these. It only decides before sending.",
          "You want to confirm by a receipt that a sent notification arrived. Checking delivery receipts is not included.",
          "You want to check the payload signature itself. Whether a payload is signed is decided from the boolean the caller passes (payload_signed).",
          "You use it with the idempotency key or signature requirement turned off. Setting require_idempotency_key or require_signed_payload to false removes that check.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the recipient. A notification to anything outside the set of pinned recipients is judged not to be sent (sending to arbitrary URLs is rejected; SSRF).",
          "It checks the event. A notification of an event type that is not allowed is judged not to be sent.",
          "It checks the idempotency key. A notification without an idempotency key is judged not to be sent (when require_idempotency_key is at its default, true).",
          "It checks the signature. A notification whose payload_signed is not true (unsigned) is judged not to be sent (when require_signed_payload is at its default, true).",
          "It checks for duplicates. A notification whose key is in the given set of idempotency keys already sent is judged a duplicate send and not to be sent."
        ],
        "what_it_does_not_do": [
          "It does not send Webhooks. It does not resend on failure. It does not sign payloads. The user does these.",
          "It does not check delivery receipts. This Tsugite only decides before sending.",
          "The idempotency key and signature requirements can be turned off in configuration. Setting require_idempotency_key or require_signed_payload to false removes that check, and a missing idempotency key or an unsigned payload no longer stops a notification (both default to true).",
          "It does not keep the set of idempotency keys already sent (delivered). The caller passes it.",
          "The actual delivery API and signature scheme have not been decided. They are set in config at deployment.",
          "Delivery consent and approval paths, and delivery provenance, belong to the Kernels (consent-facts and provenance-facts).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a notification API."
        ],
        "integration": "Input: call evaluate(cfg, request, delivered) in src/guards.js (a pure function that decides before sending). Pass request as { recipient, event_type, idempotency_key, payload_signed } and delivered as the set of idempotency keys already sent ({ key: 1 }); cfg holds allowed_recipients, allowed_events, require_idempotency_key and require_signed_payload. Returns: { ok: true, errors: [] } if it may be sent, otherwise { ok: false, errors: [...] }. User provides: sending, resending, signing and checking receipts (there is no port).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-05",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Delivery and read confirmation — Messaging Extension Tsugite",
        "summary": "The Messaging Extension Tsugite (継手, tsugite) checks a delivery-evidence query (a DeliveryEvidence request) against the contract's types before carrying it to the backing service, and matches the returned receipt against the contract. It treats each stage (requested, delivered, read and acknowledged) as separate evidence, and does not mistake delivery for business acceptance (fail-closed).",
        "problem": "When recording delivered and read as facts, it prevents accepting evidence for a stage other than the one queried, and deriving business acceptance from delivery evidence.",
        "use_when": [
          "You want to query the backing service for delivered, read and acknowledged as evidence per stage, and record them only after matching.",
          "You do not want confirmation of a higher stage to be taken to imply confirmation of a lower stage or business acceptance.",
          "For consent or audit, you want to keep the merely-requested state and the delivered state separate.",
          "You want it to stop there when a receipt is inconsistent (HALTED).",
          "You do not want the same query sent twice. When resending before a receipt is settled, the send is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to confirm that the receiving side processed it (accepted it as business). This Tsugite does not confirm that.",
          "You want to include the message body. This Tsugite does not handle message contents; it carries only references.",
          "You want to send the notifications themselves. Sending is handled by the mail Tsugite (D-01), the SMS and push Tsugite (D-02 and D-03) and the notify Tsugite (D-04).",
          "You want to treat delivery as approval or a sign-off. Delivered is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backing service you can use right away. No actual backing service (which delivery-evidence mechanism) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "It accepts only evidence for the stage queried (evidence_kind). A receipt that returns evidence for a different stage is not accepted, and it stops.",
          "A receipt that claims business acceptance (one that derives business acceptance from delivery evidence) is not accepted, and it stops.",
          "It checks references. Empty references, references with a path (containing ..) and additional fields are rejected. An empty list allows nothing.",
          "It checks the side effect. A delivery-evidence query has no side effect (NONE). Receipts that increase the external effect and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted. After a receipt that cannot be matched, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not include a mechanism that produces delivery evidence itself. It only carries delivery-evidence queries to the backing service and matches the returned receipt. Which delivery-evidence mechanism to connect to is undecided and will be settled at a separate gate when connecting.",
          "Delivered, read and acknowledged are not business acceptance. It does not confirm that the receiving side processed it.",
          "Judging business acceptance is the role of the Kernels and of people. This Tsugite only checks before sending and after receiving.",
          "Delivered is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific queue or messaging platform."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/messaging-ext/v1. Input: pass a DeliveryEvidence request to submit(request) on createMessagingExtClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries evidence_kind, evidence_state, confirmed and business_acceptance in its result. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and the pinned lists in cfg. Side effect: NONE (no side effect).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-06",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Bounce and complaint records — Messaging Extension Tsugite",
        "summary": "The Messaging Extension Tsugite (継手, tsugite) checks records of undeliverable messages (bounces) and complaints (a BounceComplaint request) against the contract's types before carrying them to the backing service, matches the returned receipt against the contract, and returns whether the recipient was suppressed (suppression_state and suppressed). It does not treat a temporary deferral (DEFERRAL) as a reason to suppress, and if any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When recording bounces and spam reports (complaints), it prevents treating even a temporary deferral as a reason to suppress a recipient, accepting a receipt whose effect is unknown or a receipt for a different request, and sending the same record twice.",
        "use_when": [
          "You want to record bounces and spam reports (complaints) on the backing service and receive the suppressed state (suppression_state and suppressed).",
          "You do not want a recipient suppressed because of a temporary deferral (DEFERRAL).",
          "You want to hand suppressed recipients to the sending Tsugite (mail, SMS and push) on your side. This Tsugite returns the state for doing so.",
          "You want it to stop there when a receipt is inconsistent (HALTED)."
        ],
        "do_not_use_when": [
          "You want it to take care of stopping sends as well. This Tsugite only returns the state; handing suppressed recipients to the sending Tsugite (mail, SMS and push) is the user's role.",
          "You want to include the message body. This Tsugite does not handle message contents; it carries only references.",
          "You want to treat a suppression as approval or a sign-off. No result is approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backing service you can use right away. No actual backing service (which queue, publish-subscribe or delivery-evidence mechanism) is included, and it has not been decided yet."
        ],
        "what_it_checks": [
          "A record that tries to suppress a recipient because of a temporary deferral (DEFERRAL) is rejected. DEFERRAL and records that do not suppress (NOT_SUPPRESSED) have no side effect (NONE).",
          "It checks the side effect. The side effect of recording a bounce or complaint is NONE or EXTERNAL_REVERSIBLE. Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "It checks references. Empty references, references with a path (containing ..) and additional fields are rejected. An empty list allows nothing.",
          "A receipt for a different request (a spoofed request_digest) is not accepted. After a receipt that cannot be matched, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not hand suppressed recipients to the sending Tsugite (mail, SMS and push). This Tsugite only returns the state.",
          "It carries bounce and complaint records and does not include functions for judging the contents of complaints or managing sender reputation.",
          "Deciding the policy for suppressing and resending is the role of the Kernel (policy-facts) and of people. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual backing service. The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "No result is approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific queue or messaging platform."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/messaging-ext/v1. Input: pass a BounceComplaint request to submit(request) on createMessagingExtClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries event, suppression_state and suppressed in its result. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and the pinned lists in cfg. Side effect: NONE or EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-07",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Message queue — Messaging Extension Tsugite",
        "summary": "The Messaging Extension Tsugite (継手, tsugite) checks queue operations (a Queue request: ENQUEUE, DEQUEUE or ACK) against the pinned queues and the idempotency key before carrying them to the backing service, and matches the returned receipt against the contract. It does not enqueue twice, and does not mistake enqueuing for delivery. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When a queue is used for asynchronous hand-off, it prevents enqueuing to a queue that is not pinned, enqueuing the same message twice, enqueuing different content under the same idempotency key, and mistaking enqueuing for processing by the receiving side.",
        "use_when": [
          "For asynchronous hand-off, you want to carry enqueue (ENQUEUE), dequeue (DEQUEUE) and ACK only for pinned queues.",
          "You do not want the same message enqueued twice. idempotency_key is required for ENQUEUE, and a duplicate (DEDUP_HIT) is treated as having no new effect.",
          "You want to confirm that the receipt's state answers the operation (op) before accepting it.",
          "You want it to stop there when a receipt is inconsistent (HALTED)."
        ],
        "do_not_use_when": [
          "You want to confirm that the dequeuing side processed it (accepted it as business). This Tsugite does not confirm that.",
          "You want to include the message body. This Tsugite does not handle message contents; it carries only references.",
          "You want to treat enqueuing as approval or a sign-off. Enqueued is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backing service you can use right away. No actual backing service (which queue mechanism) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "Requests to a queue that is not pinned are not sent.",
          "It checks references. Empty references, references with a path (containing ..) and additional fields are rejected. An empty list allows nothing.",
          "An ENQUEUE without idempotency_key is not sent. A request with the same idempotency key but different content is not sent (REFUSED).",
          "A receipt that is a duplicate (DEDUP_HIT) yet carries a new effect, and a DEDUP_HIT for DEQUEUE, are not accepted.",
          "A receipt whose state does not answer the operation is not accepted (only ENQUEUE→ENQUEUED or DEDUP_HIT, DEQUEUE→DEQUEUED, ACK→ACKED, or FAILED).",
          "It checks the side effect. Queue operations are EXTERNAL_REVERSIBLE (NONE for DEDUP_HIT and FAILED). Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted. After a receipt that cannot be matched, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not include the queue itself. It is a Tsugite that carries enqueue and dequeue requests. Which backing service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "Enqueuing and dequeuing do not mean the receiving side processed it (accepted it as business). It does not confirm that.",
          "Detecting duplicates and deciding the policy for suppressing and resending are the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and after receiving.",
          "Enqueued is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific queue or messaging platform."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/messaging-ext/v1. Input: pass a Queue request to submit(request) on createMessagingExtClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries queue_state in its result. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_REVERSIBLE (NONE for DEDUP_HIT and FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-08",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Pub/Sub fan-out — Messaging Extension Tsugite",
        "summary": "The Messaging Extension Tsugite (継手, tsugite) checks publish and subscribe operations (a PubSub request: PUBLISH, SUBSCRIBE or UNSUBSCRIBE) against the pinned topics and event schemas before carrying them to the backing service, and matches the returned receipt against the contract. It does not mistake publishing for being received (received: NOT_OBSERVED). If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When publish and subscribe are used for one-to-many delivery, it prevents publishing or subscribing to topics that are not pinned, publishing events whose schema is not pinned, and mistaking publishing for the subscribers having received it.",
        "use_when": [
          "You want to carry publish (PUBLISH), subscribe (SUBSCRIBE) and unsubscribe (UNSUBSCRIBE) only for pinned topics.",
          "You want every published event to carry a pinned event schema (event_schema_ref).",
          "You want to handle publishing and being received separately.",
          "You do not want the same publish sent twice. When resending before a receipt is settled, the send is resolved by a lookup.",
          "You want it to stop there when a receipt is inconsistent (HALTED)."
        ],
        "do_not_use_when": [
          "You want to confirm that subscribers received or processed it. received is not observed (NOT_OBSERVED), and it does not confirm business acceptance either.",
          "You want to include the message body. This Tsugite does not handle message contents; it carries only references.",
          "You want to treat publishing as approval or a sign-off. Published is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backing service you can use right away. No actual backing service (which publish-subscribe mechanism) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "Requests for a topic or an event schema that is not pinned are not sent.",
          "A PUBLISH without an event schema (event_schema_ref) is not sent.",
          "It checks references. Empty references, references with a path (containing ..) and additional fields are rejected. An empty list allows nothing.",
          "A receipt whose state does not answer the operation is not accepted (only PUBLISH→PUBLISHED, SUBSCRIBE→SUBSCRIBED, UNSUBSCRIBE→UNSUBSCRIBED, or FAILED).",
          "It checks the side effect. Publish and subscribe are EXTERNAL_REVERSIBLE (NONE for FAILED). Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted. After a receipt that cannot be matched, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It is a Tsugite that carries publish and subscribe requests to the backing service, and does not include the one-to-many distribution (fan-out) function itself.",
          "Publishing is not being received (received: NOT_OBSERVED).",
          "It does not include an actual backing service. Which publish-subscribe mechanism to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "Judging business acceptance is the role of the Kernels and of people. This Tsugite only checks before sending and after receiving.",
          "Published is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific queue or messaging platform."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/messaging-ext/v1. Input: pass a PubSub request to submit(request) on createMessagingExtClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries pubsub_state and received: NOT_OBSERVED in its result. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_REVERSIBLE (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "D-09",
        "category": "D/通知・配信",
        "kind": "Tsugite",
        "title": "Dead-letter handling — Messaging Extension Tsugite",
        "summary": "The Messaging Extension Tsugite (継手, tsugite) checks dead-letter operations (a DeadLetter request: PARK, REDRIVE or PURGE) against the pinned dead-letter queues (dlq_refs) before carrying them to the backing service, and matches the returned receipt against the contract. It carries an irreversible deletion (PURGE) only when the setting allow_purge: true is present, and if any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When dead-letter queues are used to set failed messages aside, it prevents operating on dead-letter queues that are not pinned, performing an irreversible deletion (PURGE) that the settings do not allow, and accepting a receipt whose effect is larger than intended.",
        "use_when": [
          "You want to carry PARK and REDRIVE to pinned dead-letter queues to set failed messages aside.",
          "You want to carry an irreversible deletion (PURGE) only when it has been explicitly allowed in the settings (allow_purge defaults to false).",
          "You do not want the same operation sent twice. When resending before a receipt is settled, the send is resolved by a lookup.",
          "You want it to stop there when a receipt is inconsistent (HALTED)."
        ],
        "do_not_use_when": [
          "You want it to judge whether something may be deleted. Judging whether something may be deleted is the role of the Kernels and of people.",
          "You want to include the message body. This Tsugite does not handle message contents; it carries only references.",
          "You want to treat a successful set-aside or deletion as approval or a sign-off. No result is approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backing service you can use right away. No actual backing service (which dead-letter mechanism) is included, and it has not been decided."
        ],
        "what_it_checks": [
          "Requests to a dead-letter queue that is not pinned are not sent.",
          "A PURGE is not sent unless the setting allow_purge: true is present.",
          "It checks references. Empty references, references with a path (containing ..) and additional fields are rejected. An empty list allows nothing.",
          "A receipt whose state does not answer the operation is not accepted (only PARK→PARKED, REDRIVE→REDRIVEN, PURGE→PURGED, or FAILED).",
          "It checks the side effect. PARK and REDRIVE are EXTERNAL_REVERSIBLE, PURGE is EXTERNAL_IRREVERSIBLE, and FAILED is NONE. Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted. After a receipt that cannot be matched, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "What PURGE deletes cannot be undone (EXTERNAL_IRREVERSIBLE). This Tsugite only checks whether the settings allow it.",
          "It is a Tsugite that carries dead-letter operation requests, and does not include a function that finds failed messages and sets them aside on its own.",
          "Judging whether something may be deleted is the role of the Kernels and of people. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual backing service. The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "No result is approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific queue or messaging platform."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/messaging-ext/v1. Input: pass a DeadLetter request to submit(request) on createMessagingExtClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries dlq_state in its result. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_REVERSIBLE for PARK and REDRIVE; EXTERNAL_IRREVERSIBLE for PURGE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-01",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Scheduled trigger (cron) — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As a scheduled trigger (cron), it checks requests to register, update or disable a schedule (ScheduleTrigger), sends them to the execution platform's scheduler, and matches the schedule state and the next fire in the receipt. The firing itself is done by the execution platform.",
        "problem": "When daily or hourly runs are left to an external scheduler, it prevents registering unpinned schedules or trigger specs, misreading a disabled schedule as still having a next fire, and treating a schedule as registered without matching the receipt. It is a check so that a successful registration is not mistaken for business authorization.",
        "use_when": [
          "You want requests to register, update or disable daily or hourly schedules on an external scheduler to be checked against pinned schedules (schedule_refs) and trigger specs (trigger_spec_refs) before sending.",
          "You do not want to trust the scheduler's receipt as is, and want to match that the receipt's state answers the request (op) (REGISTER→REGISTERED, UPDATE→UPDATED, DISABLE→DISABLED) or is a FAILED with no effect before using it.",
          "You do not want to send the same request twice. A send whose result was unknown is resolved by a lookup.",
          "You want further sends to stop (HALTED) when a receipt cannot be matched."
        ],
        "do_not_use_when": [
          "You want to confirm that the schedule fired on time. This Tsugite only looks at the registration receipt, not at whether it fired.",
          "You want a scheduler itself. No actual execution platform is included, and which scheduler to connect to has not been decided.",
          "You want to treat a registered schedule as business authorization to run that job. Deciding what may run and when is the role of the Kernel (policy-facts).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned schedules or trigger specs, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It accepts only a receipt state that answers the request (op) (REGISTER→REGISTERED, UPDATE→UPDATED, DISABLE→DISABLED) or a FAILED with no effect (no side effect and no next fire). It does not accept any other state.",
          "It does not accept a receipt in which a disabled or failed schedule carries a next fire (next_fire_ref).",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not check whether the schedule fired on time (it looks only at the registration receipt). The firing itself is done by the execution platform.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "Enqueued, executed and completed are not approval. Every result carries approval: false.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a ScheduleTrigger request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries schedule_state and next_fire_ref. User provides: a port to the execution platform (submit, and optionally lookup) and cfg schedule_refs and trigger_spec_refs. Side effect: EXTERNAL_REVERSIBLE (NONE for duplicates, denials and failures).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-02",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Delayed job — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As a delayed job, it checks a request for work to run N minutes later (DelayedJob) against the pinned jobs, sends it to the execution platform, and matches the enqueue state (enqueue_state) and the worker assignment (worker_assignment_ref) in the receipt. Waiting and then running the work is done by the execution platform.",
        "problem": "When work to run later, such as a reminder, is handed to an external execution platform, it prevents enqueuing unpinned jobs, enqueuing the same work twice, and misreading a failed enqueue as having a worker assigned. It is a check so that a successful enqueue is not mistaken for business authorization.",
        "use_when": [
          "You want to hand work to run N minutes later to an external execution platform and send only pinned jobs (job_refs).",
          "You do not want the same work enqueued twice. A duplicate (DEDUP_HIT) is taken as having no new effect, and different content is never sent under the same idempotency key.",
          "You do not want to trust the execution platform's receipt as is, and want to match it against the contract before treating the work as enqueued.",
          "You want to resolve a send whose result was unknown by a lookup, without sending it twice."
        ],
        "do_not_use_when": [
          "You want to confirm that the enqueued work ran at the scheduled time. This Tsugite only looks at the enqueue receipt, not at whether the work ran.",
          "You want the mechanism that waits and runs the work itself. No actual execution platform is included, and which mechanism to connect to has not been decided.",
          "You want to treat a successful enqueue as business authorization to run that work. Deciding what may run and when is the role of the Kernel (policy-facts).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned jobs, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It does not send a request that reuses an idempotency key with different content. It does not accept a receipt in which a duplicate (DEDUP_HIT) carries a new effect.",
          "It does not accept a receipt in which a failed enqueue carries a worker assignment (worker_assignment_ref).",
          "When the receipt's state does not answer the request, it does not accept the receipt.",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not check whether the enqueued work ran (it looks only at the enqueue receipt). Waiting and running are done by the execution platform.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "Enqueued, executed and completed are not approval. Every result carries approval: false.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a DelayedJob request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries enqueue_state and worker_assignment_ref. User provides: a port to the execution platform (submit, and optionally lookup) and cfg job_refs. Side effect: EXTERNAL_REVERSIBLE (NONE for duplicates, denials and failures).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-03",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Task queue and worker — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As a task queue and worker, it checks a request for a heavy task (TaskEnqueue) and its content bytes against the pinned queues, the content digest and the size limit, sends them to the execution platform's queue, and matches the enqueue state and the worker assignment in the receipt. Running the task is done by the execution platform.",
        "problem": "When heavy work is put on a queue to run in the background, it prevents enqueuing content that was tampered with on the way or is too large, enqueuing the same task twice, and misreading a failed enqueue as having a worker assigned.",
        "use_when": [
          "You want to enqueue tasks only to pinned queues (queue_refs) to run heavy work in the background.",
          "You want to check the digest and size limit (max_payload_bytes) of the content (bytes) yourself before sending it to the queue.",
          "You do not want the same task enqueued twice. A duplicate (DEDUP_HIT) is taken as having no new effect, and different content is never sent under the same idempotency key.",
          "You want to resolve a send whose result was unknown by a lookup, without sending it twice."
        ],
        "do_not_use_when": [
          "You want to confirm that the enqueued task ran. This Tsugite only looks at the enqueue receipt, not at whether the task ran.",
          "You want a queue mechanism itself. No actual execution platform is included, and which queue to connect to has not been decided.",
          "You want to treat a successful enqueue as business authorization to run that work. Deciding what may run and when is the role of the Kernel (policy-facts).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned queues, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It checks the content. When the request's payload_digest (target: task_ref) does not match the content bytes (tampering), it does not send. TaskEnqueue requires bytes.",
          "It does not send content that exceeds the limit (max_payload_bytes).",
          "It does not send a request that reuses an idempotency key with different content. It does not accept a receipt in which a duplicate (DEDUP_HIT) carries a new effect.",
          "It does not accept a receipt in which a failed enqueue carries a worker assignment (worker_assignment_ref).",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not check whether the enqueued task ran (it looks only at the enqueue receipt). The task is run by the execution platform.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "Enqueued, executed and completed are not approval. Every result carries approval: false.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a TaskEnqueue request and its bytes (required) to submit(request, bytes) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries enqueue_state and worker_assignment_ref. User provides: a port to the execution platform (submit, and optionally lookup) and cfg queue_refs and max_payload_bytes (required). Side effect: EXTERNAL_REVERSIBLE (NONE for duplicates, denials and failures).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-04",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Workflow orchestration (saga) — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As workflow orchestration (saga), it checks requests to move a saga step (SagaStep) forward (FORWARD) or to compensate it (COMPENSATE) against the pinned sagas and steps, sends them to the execution platform, and matches the step state in the receipt. Coordinating the saga is done by the execution platform, and a successful compensation is not mistaken for a person's approval of the cancellation.",
        "problem": "When a multi-step process fails midway and is rolled back, it prevents sending a compensation without a compensation-step reference, sending steps with an unknown effect or irreversible steps without permission, and treating successful compensation as a person's approval of the cancellation.",
        "use_when": [
          "You want to send the steps of a multi-step process only with pinned sagas (saga_refs) and steps (step_refs, including compensation steps).",
          "When rolling back after a midway failure, you want to send compensation requests (COMPENSATE) only with a compensation-step reference.",
          "You want to match that the step state in the receipt answers the request (FORWARD→EXECUTED, FAILED or SKIPPED; COMPENSATE→COMPENSATED, FAILED or SKIPPED).",
          "You want to send steps with irreversible effects only when explicitly set in the configuration (allow_irreversible: true). The default is false."
        ],
        "do_not_use_when": [
          "You want to treat successful compensation as a person's approval of the cancellation. Approval belongs to the Kernels and to people; from the receipt it accepts only NOT_DERIVED.",
          "You want to treat a successfully executed step as business authorization. It does not accept receipts that derive business authorization.",
          "You want saga coordination (the state machine for multi-step processes) itself. Coordination is on the execution platform side; no actual execution platform is included, and which one to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned sagas or steps, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It does not send a compensation (COMPENSATE) without a compensation-step reference.",
          "It does not send steps whose intended effect is UNKNOWN. It sends steps with irreversible effects only when the configuration sets allow_irreversible: true.",
          "When the step state in the receipt does not answer the request, it does not accept the receipt.",
          "It does not accept receipts that derive compensation approval or business authorization (it accepts only NOT_DERIVED).",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "Saga coordination (the state machine for multi-step processes) is the role of the execution platform; this Tsugite does not have it. It does not roll back either; it only checks and sends compensation requests and matches the receipts.",
          "Successful compensation is not a person's approval of the cancellation. No success is approval, and every result carries approval: false.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a SagaStep request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries step_state, compensation_authorization and business_authorization. User provides: a port to the execution platform (submit, and optionally lookup) and cfg saga_refs, step_refs and allow_irreversible. Side effect: as intended for each step (NONE for SKIPPED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-05",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Distributed lock — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As a distributed lock, it checks requests to acquire (ACQUIRE), renew (RENEW) or release (RELEASE) a lock (Lock) against the pinned locks, this Tsugite's holder (holder_ref) and a TTL within the limit, sends them to the execution platform, and matches the lock state in the receipt. Mutual exclusion itself is the execution platform's role, and it does not do leader election.",
        "problem": "When mutual exclusion for concurrent runs is left to an external lock mechanism, it prevents touching another holder's lock, taking a lock with no TTL or a TTL over the limit, and misreading an unknown result (UNKNOWN) as \"held\". It is a check so that a lock not held is never treated as held.",
        "use_when": [
          "You leave mutual exclusion for concurrent runs to an external lock mechanism and want to handle pinned locks (lock_refs) only with this Tsugite's holder (holder_ref).",
          "You want locks to have a TTL and to send only ACQUIRE and RENEW requests that do not exceed the TTL limit (max_lock_ttl_seconds).",
          "You want \"held\" (held: true) only when the receipt's state is ACQUIRED or HELD. It is never derived from UNKNOWN.",
          "You want to accept a fencing token (fencing_token_ref) only in receipts for a lock that is held (ACQUIRED or HELD)."
        ],
        "do_not_use_when": [
          "You want to use it for leader election (E-06). This Tsugite's contract has no leader election kind, and it has neither election terms nor observation of other holders. E-06 is handled by the Component that bundles this Tsugite's distributed locks (the leader election part).",
          "You want to rely on this Tsugite for correct mutual exclusion. The correctness of lock exclusion is the role of the execution platform; this Tsugite only checks the consistency of the holder, the TTL and the receipt.",
          "You want a lock mechanism itself. No actual execution platform is included, and which lock mechanism to connect to has not been decided.",
          "You want to treat holding a lock as business authorization to run that work. Deciding what may run and when is the role of the Kernel (policy-facts).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned locks, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It does not send lock requests for a holder other than this Tsugite's holder. If another holder has the lock, the result is DENIED and is taken as having no effect.",
          "It does not send ACQUIRE or RENEW without a TTL, or requests with a TTL over the limit (max_lock_ttl_seconds).",
          "When the receipt's state does not answer the request (other than ACQUIRE→ACQUIRED, HELD, DENIED, UNKNOWN; RENEW→HELD, EXPIRED, DENIED, UNKNOWN; RELEASE→RELEASED, EXPIRED, DENIED, UNKNOWN), it does not accept the receipt.",
          "It does not accept receipts with a fencing token on a lock that is not held, such as a released lock. It never derives \"held\" from UNKNOWN.",
          "It does not accept requests of a leader election (E-06) kind, because the contract has no such kind.",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN), and stops (HALTED) on a receipt it cannot match. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not do leader election. E-06 is handled by the Component that bundles this Tsugite (the leader election part). This Tsugite itself has no election kind.",
          "The correctness of lock exclusion is the role of the execution platform. This Tsugite only checks the consistency of the holder, the TTL and the receipt.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "Enqueued, executed and completed are not approval. Every result carries approval: false.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a Lock request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries lock_state, held and fencing_token_ref. User provides: a port to the execution platform (submit, and optionally lookup) and cfg lock_refs, holder_ref and max_lock_ttl_seconds. Side effect: EXTERNAL_REVERSIBLE (NONE for duplicates, denials and failures).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-06",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Component",
        "title": "Leader election — Leader election part (Component)",
        "summary": "The Leader election part (Component) is a part that bundles the E-05 distributed lock Tsugite to elect a leader, so that only one runs in a redundant setup. It campaigns, renews before the deadline and resigns, and answers \"leader\" only when it holds the lock, has a fencing token, and is before the lease deadline (the time before sending + ttl − safety margin). On a denial, an expiry, an unknown result, a failure or the clock going backwards, it immediately stops being the leader (fail-closed). Being the leader is not authority.",
        "problem": "Behind availability, it prevents two participants contending for the same lock from both claiming to be the leader at the same time (when the lock infrastructure is correct), seeing the deadline as longer by the delay before the lock's result comes back, and claiming to be the leader without a fencing token.",
        "use_when": [
          "You want only one participant to run the work in a redundant setup. Inject a client of the E-05 distributed lock Tsugite (one that has submit(LockRequest)) as lock.",
          "You want to campaign (campaign), renew before the deadline (renew), resign (resign) and check whether it is the leader now (status), with operations that do not throw an exception.",
          "You want to receive a fencing token (fencing_token_ref) while it is the leader and pass it to the write target. When leader is true, fencing_token_ref is always present.",
          "You want to estimate the deadline conservatively as the time read before requesting the lock + ttl − safety margin. The time is taken only from the injected monotonic clock (clock.now()).",
          "You want it to stop being the leader locally at once when it resigns, regardless of the result of releasing the lock."
        ],
        "do_not_use_when": [
          "You want to use being the leader as authority or approval. Every result carries approval: false and authority: NOT_DERIVED.",
          "You want this part to stop writes by an old leader. Stopping writes by an old leader with a fencing token is the role of the write target; this part only passes the token on.",
          "You want to treat \"leader\" as assurance of the latest state of the lock infrastructure. \"Leader\" is this participant's local judgement.",
          "You want to stay the leader across a restart. The state is only inside this process, and after a restart it is not the leader until it campaigns again.",
          "You need a part checked against a real lock infrastructure. It has never connected to a real infrastructure and was checked only with a stand-in lock.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed (ttl outside 1 to 3600, a safety margin of ttl or more, a lock without submit, a clock without now, a reference containing \"..\"), it stops with INVALID_OPTIONS.",
          "When the previous operation has not finished, it stops the second one with BUSY (only one operation at a time).",
          "When it tries to renew while not the leader, it stops with NOT_LEADER, and on a renewal after the deadline with LEASE_EXPIRED, without sending to the lock. When the lock had expired on the infrastructure's side, the result is also LEASE_EXPIRED.",
          "When another participant holds the lock, it returns LOCK_DENIED, and when the result is not DONE (UNKNOWN, REFUSED, ALREADY_DONE, HALTED and so on) or held is not true, it returns LOCK_UNKNOWN, and it is not the leader.",
          "When it holds the lock but there is no fencing token, it returns NO_FENCING_TOKEN, and when the lock client throws an exception, it returns PORT_FAILED, and it does not claim to be the leader.",
          "When the clock goes backwards, it discards the lease and stops with CLOCK_WENT_BACKWARDS.",
          "It extends the deadline only when status is DONE, held is true and there is a fencing token. Fuzzing checked every time that two participants contending for the same lock never claim to be the leader at the same time."
        ],
        "what_it_does_not_do": [
          "The Leader election part is not a Tsugite. It has no Request or Receipt of its own; the external effect of acquiring, renewing and releasing the lock is caused by the bundled E-05 Tsugite under its own contract (workflow-jobs/v1). This part itself does not touch the outside.",
          "The meaning of the bundled E-05 Tsugite (matching the receipt, not approval) stays on the Tsugite's side.",
          "Being the leader is not authority and is not approval (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "It does not guard writes with the fencing token. Stopping writes by an old leader is the role of the write target.",
          "It does not check the correctness of the lock infrastructure (linearizability, clock skew, behaviour during a partition). Safety relies on that correctness and on the safety margin estimate the caller decides.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular coordination service."
        ],
        "integration": "Create it with createLeaderElection(options) in src/leader.js. Input: lock_ref, holder_ref, ttl_seconds, renew_margin_ms, lock, and an optional clock and timestamp. Returns: { ok, errors, instance }; campaign, renew and resign (Promise) and status return { ok, errors, leader, fencing_token_ref, approval: false, … }. User provides: lock.submit(request) (the client of the E-05 Tsugite), injected into this part. Side effect: caused by the bundled Tsugite (EXTERNAL_REVERSIBLE); credentials are held by the injected client.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-07",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Debounce and throttle — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As debounce and throttle, it checks a throttle (Throttle) request against the pinned throttle policies, sends it to the execution platform, and matches the decision in the receipt (decision, allowed and retry_after_ref). The throttle decision itself is made by the execution platform.",
        "problem": "When the decision to suppress repeated clicks or multiple launches is left to an external execution platform, it prevents querying with an unpinned policy, accepting an inconsistent receipt in which an allow (ALLOW) decision carries a retry time (retry_after_ref), and using a decision without matching the receipt.",
        "use_when": [
          "You want to query the execution platform for decisions to suppress repeated clicks and multiple launches only with pinned throttle policies (throttle_policies).",
          "You do not want to trust the execution platform's decision as is, and want to match the request and receipt (request_id, request_digest) before using it.",
          "You want throttle queries to be treated as having no external effect (NONE). Receipts that carry an effect are not accepted."
        ],
        "do_not_use_when": [
          "You expect this Tsugite to make the throttle decision itself. The decision is the role of the execution platform; no actual execution platform is included, and which one to connect to has not been decided.",
          "You need a request kind dedicated to debounce. The contract's kind is Throttle; there is no kind dedicated to debounce.",
          "You want to treat an allowed decision (allowed) as business authorization to run that work. Deciding what may run and when is the role of the Kernel (policy-facts).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned throttle policies, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "It does not accept a receipt in which an allow (ALLOW) decision carries a retry time (retry_after_ref).",
          "When the receipt's state does not answer the request, it does not accept the receipt.",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not make throttle decisions. The decision is made by the execution platform; this Tsugite only checks and sends the request and matches the decision in the receipt.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "Enqueued, executed and completed are not approval. Every result carries approval: false.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a Throttle request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries decision, allowed and retry_after_ref. User provides: a port to the execution platform (submit, and optionally lookup) and cfg throttle_policies. Side effect: none (NONE).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-08",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Tsugite",
        "title": "Job status query — Job Control Tsugite",
        "summary": "The Job Control Tsugite (継手, tsugite) checks requests (Request) to the execution platform against pinned lists before sending them, and matches the returned receipts against the contract. As a job status query, it checks a job status (JobStatus) query against the pinned jobs, sends it to the execution platform, and matches the status (status), whether it can be retried (retryable) and the failure reference (failure_ref) in the receipt. It does not mistake completion for business acceptance, or \"retryable\" for retry authorization.",
        "problem": "When asking the execution platform for a job's status to answer \"is it done?\", it prevents mistaking completion for business acceptance or retryable for retry authorization, and accepting a failure reference that cannot be traced.",
        "use_when": [
          "You want to query the execution platform about progress and completion only with pinned jobs (job_refs).",
          "You want to accept a failure reference (failure_ref) only when it resolves to an ai-execution-error record.",
          "You want to keep completion and retryable separate from business acceptance and retry authorization. From the receipt it accepts only NOT_DERIVED.",
          "You want queries to be treated as having no external effect (NONE). Receipts that carry an effect are not accepted."
        ],
        "do_not_use_when": [
          "You want to check the content of the failure record. It does not check the content of the ai-execution-error record that failure_ref points to (only whether it resolves).",
          "You want to treat completion as business acceptance, or retryable as retry authorization. Both belong to the Kernels and to people.",
          "You want a mechanism that holds job status itself. No actual execution platform is included, and which one to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks references. It does not send unpinned jobs, empty references, or references containing a path or \"..\". An empty list allows nothing.",
          "When failure_ref cannot be resolved, or there is no means to resolve it (port.resolveFailureRef), it does not accept the receipt and stops thereafter (HALTED).",
          "It accepts failure_ref only for FAILED or CANCELLED.",
          "It does not accept receipts that derive retry authorization or business acceptance (it accepts only NOT_DERIVED).",
          "When the receipt's state does not answer the request, it does not accept the receipt. It never derives execution or authorization from an UNKNOWN state.",
          "When the receipt's request_id, request_digest or references do not match the request (request_digest spoofing), it does not accept the receipt.",
          "It does not accept receipts that increase the external effect or whose effect is unknown (UNKNOWN). The effect must be no more than the request's intent on both axes: how far it reaches and whether it can be undone.",
          "If a receipt cannot be matched, it stops (HALTED) and sends nothing further. It never sends the same request_id twice; a failure of the execution platform is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not check the content of the ai-execution-error record that failure_ref points to (only whether it resolves).",
          "Completion is not business acceptance, and retryable is not retry authorization. No success is approval, and every result carries approval: false.",
          "Deciding what may run and when (policy-facts) and detecting duplicates (reconcile-facts) are the roles of the Kernels. Business authorization, retry authorization and compensation approval belong to the Kernels and to people; this Tsugite only checks before sending and after receiving.",
          "It does not include an actual execution platform (which scheduler, queue or lock mechanism). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific scheduler or queue."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/workflow-jobs/v1. Input: pass a JobStatus request to submit(request) on createWorkflowClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries status, retryable, failure_ref, retry_authorization and business_acceptance. User provides: a port to the execution platform (submit, optionally lookup, and resolveFailureRef, which is required when there is a failure_ref) and cfg job_refs. Side effect: none (NONE).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-09",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Component",
        "title": "Read-only aggregation run — Read-only aggregation run part (Component)",
        "summary": "The Read-only aggregation run part (Component) is a part that checks and reads its inputs and writes the aggregation result as a secondary copy. Every input is read with the C-01 Tsugite with the expected sha256 attached, and only inputs whose digest matches are read as JSON. The aggregator (fn) receives a frozen copy. The output of fn is turned into RFC 8785 bytes and written, together with the lineage record (the H-10 part (Primitive)), to content-addressed keys with the idempotent write Tsugite (C-05). fn runs inside the same process; this is not isolation for security.",
        "problem": "Which inputs the numbers in an aggregate table came from does not show in the table. This part keeps the set of inputs, the aggregator's version and the hash of the output as lineage. It also prevents aggregating with inputs that have not been checked, letting the aggregator rewrite stored objects, and overwriting the location of an input with the output.",
        "use_when": [
          "You want to check and read 1 to 1000 inputs (key and sha256), aggregate them with a trusted aggregator fn, and write the output once to out/<tool ref>/<digest of the input set>/<sha256 of the output>.json (run).",
          "You want to pass fn only a frozen array [{ key, sha256, data }] in key order, and not the original bytes or the values that were read themselves.",
          "You want to keep, next to the output, a lineage record that ties together the input Refs (<key>@sha256:<hex>), the aggregator's ref and version, and the hash of the output.",
          "You want to use another aggregator's output as the input of the next aggregation. The output location is per aggregator, and inputs that do not overlap its own output location can be read.",
          "You want to resend a write whose result is unknown as the same request (resolvePending). Doing the same run again makes the output DEDUP_HIT."
        ],
        "do_not_use_when": [
          "You want to run an untrusted aggregator in isolation. fn runs in the same process and can touch process, require, global values, the clock, random numbers and the network, and has no time or memory limit. The only thing this part protects is \"not letting the inputs be rewritten\".",
          "You want to check that the aggregation is correct or that fn is deterministic. The lineage is a record of the inputs used, the aggregator's version and the hash of the output, and is not proof that the aggregation is correct.",
          "You want fn to return fractional numbers. The output is RFC 8785 (rfc8785-restricted/v1), so fractional numbers are not accepted.",
          "You want this part to enforce size limits on inputs and outputs. The limits are set in the configuration of the read and write Tsugite.",
          "You want duplicate keys in the input JSON, or numbers beyond the safe integer range, to be checked. Inputs are read as JSON.parse reads them, and are not checked as the values passed to fn.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, it stops with INVALID_OPTIONS; when the input to run is malformed, with INVALID_INPUT; when the same key appears twice or more, with DUPLICATE_INPUT; and when an input key is the same as or under the output location out/<tool ref>, with INPUT_OUTPUT_OVERLAP; and it reads nothing.",
          "When the C-01 Tsugite returns NOT_FOUND, it reports INPUT_NOT_FOUND; when it returns GET_REJECTED or the sha256 recomputed by the part itself differs, INPUT_DIGEST_MISMATCH; when the result is unknown, READ_UNKNOWN; when the Tsugite did not send, READ_REFUSED; and on a halt or a malformed result, READ_FAILED; and it does not call fn and writes nothing.",
          "When the bytes of an input are not JSON in UTF-8 (including a BOM), it does not call fn (INPUT_NOT_JSON).",
          "When fn throws an exception (including an attempt to rewrite the frozen inputs in strict mode), it reports AGGREGATOR_FAILED, and when the value from fn cannot be canonicalized with RFC 8785 or is not a synchronous value, OUTPUT_NOT_JSON (with reason attached); and it writes nothing.",
          "When H-10 throws an exception, it reports LINEAGE_FAILED; when H-10 does not return ok: true, LINEAGE_REFUSED; and when the record's shape, count, output digest and size, tool, recorded_at or schema do not match this run, LINEAGE_INVALID; and it writes nothing.",
          "When the result of a write is unknown, it holds it as pending with WRITE_UNKNOWN and stops run with WRITE_PENDING while that is pending. When the Tsugite did not send, it reports WRITE_REFUSED, and when the Tsugite halted or the result is not APPLIED or DEDUP_HIT, WRITE_FAILED.",
          "When timestamp does not return an existing date-time in RFC 3339 UTC (Z), it reports CLOCK_FAILED; when the previous operation has not finished, BUSY; and calling resolvePending when nothing is pending returns NOTHING_PENDING."
        ],
        "what_it_does_not_do": [
          "The Read-only aggregation run part is not a Tsugite. It has no Request or Receipt of its own; the external effect of writing is caused by the bundled C-05 Tsugite under its own contract (reading is NONE). This part itself does not touch the outside.",
          "The meaning of the bundled parts (read ≠ the content is correct, lineage is not proof that the aggregation is correct, written ≠ accepted) stays on each part's side. The aggregator itself and the correctness of the aggregation are outside this part.",
          "Being written is not proof that the aggregation is correct and is not approval (approval: false, authority: NOT_DERIVED, aggregation_correctness: NOT_VERIFIED, derives_authority: false).",
          "It does not isolate fn (it is not a sandbox). It sets no time or memory limit on fn either.",
          "Whether the C-05 key and the C-01 object_ref point to the same stored object, and whether the output can later be read with C-01, is the job of the backend configuration.",
          "In this part's tests it is not connected to a real object-store or storage-lifecycle backend or to the real code of H-10. The unit tests and fuzzing use stand-ins. The state (the pending write) is only inside the process and is lost on restart.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular aggregation tool or BI product."
        ],
        "integration": "Create it with createSandboxRun(options) in src/sandbox-run.js; it returns { ok, errors, instance }. options are output_target, request_prefix, ports and an optional timestamp. run({ inputs, aggregator: { ref, version, fn } }) attaches output_key, lineage_key, written and other fields when the write succeeds, and the results carry approval: false. Ports: read.get (the client of the C-01 Tsugite), lineage (recordLineage of H-10) and write.submit (the client of the C-05 Tsugite). Side effects are caused by the bundled Tsugite, and credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "E-10",
        "category": "E/スケジュール・ワークフロー",
        "kind": "Primitive",
        "title": "Conservative-side duration projection — Conservative-side duration projection part (Primitive)",
        "summary": "The Conservative-side duration projection part (Primitive) is a standalone part that, before tasks are passed to P-05 (constraints, assignment and time slots), maps each task's estimate range, duration { min, max }, to its upper bound { min: max, max: max }. { unknown: true } is left unchanged, and the other fields are copied as they are. It keeps the original min and max in projected and returns the conversion profile and the digest of the projected input. It does not round or shorten; out-of-range and malformed values are stopped with a fixed code and position (fail-closed). It does not call P-05.",
        "problem": "Behind a schedule that comes out as \"it fits\", whether tasks were placed at the optimistic length (min) cannot be seen. P-05 places a task at the length duration.min, so passing tasks through this projection makes them be placed at the length max. For example, an indivisible task of 10 to 20 minutes is treated as 20 minutes and does not go into a 10-minute slot. Which length the judgment used can be recorded with projection_profile and projected_tasks_digest.",
        "use_when": [
          "You want to map the duration of a list of tasks (0 to 200) into a form that is placed at the upper bound of the estimate, max, before passing them to P-05 (projectDurations). Every task with { min, max } becomes { max, max }; if min = max it stays as it is.",
          "You want to keep a record, with the original values, of which tasks had their length changed. projected lists only the changed tasks, as { id, from: { min, max }, to: { min, max } }, in list order.",
          "You want to trace afterwards which input was passed to P-05. It returns the UTF-8 sha256 of the canonical JSON of the projected tasks (keys in UTF-16 code-unit order, no whitespace) as projected_tasks_digest.",
          "You want tasks whose length is unknown ({ unknown: true }) kept as they are without guessing, and their ids listed in unknown.",
          "You want the other fields of a task (requires, resource, window and so on) copied into new data with the same values. The input is not modified.",
          "You want to call it locally, as processing that does not throw an exception and has no state. It uses no clock, random numbers or environment locale, and the same input always gives the same answer."
        ],
        "do_not_use_when": [
          "You want to judge with the optimistic length (min left as it is). That judgment does not go through this part. P-05 does not use max on its own; the caller explicitly passes tasks through this projection.",
          "You want to know whether tasks can be placed. This part does not call P-05 and says nothing about whether placement is possible. P-05's answer (feasible and so on) remains P-05's fact as it is.",
          "You want the shape of tasks (requires, resource, window and so on) checked. This part only copies those fields as they are; P-05 checks them.",
          "You want to check whether the max of an estimate is correct. This part does not look at it.",
          "You want to handle more than 200 tasks, lengths over 100,000,000 minutes, or fields nested more than 32 levels deep. It stops with a fixed code.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When tasks is not a plain array (including subclasses of Array) it stops with INVALID_TASKS, and when it has more than 200 entries with TOO_MANY_TASKS.",
          "When id is missing, is a getter, is not a string of 1 to 128 code units, or contains a lone surrogate, it stops with INVALID_ID, and when the same id appears twice with DUPLICATE_ID (at the position of the second one).",
          "When duration is missing or a getter, is neither { min, max } (with no other keys) nor { unknown: true }, min or max is not an integer or is out of range, min is less than 1, or max is less than min, it stops with INVALID_DURATION. It does not round or shorten.",
          "When an element is not a plain object (a hole, null, an inherited one), when there is a symbol key or a key with a lone surrogate, when another field holds a getter, a function, undefined, a non-integer number, an unsafe integer, a non-plain object such as a Date, or a hole, or when nesting exceeds 32 levels, it stops with MALFORMED_TASK.",
          "When inspecting a value itself raises an exception (a Proxy trap and so on), it stops with INSPECTION_FAILED without throwing. Getters are not called.",
          "When it stops, it returns the code and position (path, a JSON Pointer) of the first problem found in a fixed order, and returns no projected tasks.",
          "Fuzzing (1,000,000 runs) that compares the results against an independent oracle written in a different way (isWellFormed, sorting with a comparison function, linear search of an array) confirmed that mismatches (in the projected tasks, projected, unknown, the digest, and the stopping code and path) and fail-open cases were 0.",
          "It was checked locally, with one example only, that when an indivisible task of 10 to 20 minutes and a 15-minute slot are passed to the real P-05 (solveSchedule), the result is feasible as is, and infeasible (INDIVISIBLE_TOO_LONG) after projection by this part."
        ],
        "what_it_does_not_do": [
          "The connection with P-05 was checked locally with one example only. The part's bundle does not include P-05, so this is not in the unit tests.",
          "It projects only duration. It checks neither whether the max of an estimate is correct nor the shape of the other fields of a task.",
          "It does not place anything. The projected result is not a statement of whether placement is possible, is not an approval (result_is_approval: false), and creates no authority (derives_authority: false).",
          "It has no external boundary, no state, no clock and no random numbers. Side effect: NONE, and it uses no credentials.",
          "The input and output forms were defined in v1; there is no existing implementation (new). The contract of P-05 has not been changed.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular scheduler."
        ],
        "integration": "Call projectDurations(tasks) in src/duration-projection.js (it does not throw and does not modify its input). Input: a plain array of plain objects { id, duration, ... } (up to 200 entries). Returns: { ok: true, errors: [], projection_profile, tasks, projected, unknown, projected_tasks_digest } or { ok: false, errors: [{ code, path }] }. Passing the projected tasks to P-05 is done by the caller. There is no side effect, no state, no external connection and no credential; the only dependency is the Node.js built-in crypto, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-01",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Rate limiting and quotas — Rate limiting and quota part (Primitive)",
        "summary": "The Rate limiting and quota part (Primitive) is a standalone part that limits the number or volume of calls with a per-key token bucket. It holds the bucket level as an integer and refills it without using fractions. When there are not enough tokens it returns RATE_LIMITED and the shortest retry_after_ms after which waiting makes the tokens just available, and it does not change the state (fail-closed). The state is per instance and only in this process's memory.",
        "problem": "Behind returning \"limit reached\", when limiting how fast calls are made within one process, it prevents letting extra calls through because of rounding, reducing the bucket even though there are not enough tokens, and using up memory by remembering new keys without limit.",
        "use_when": [
          "You want to limit the number or volume of calls per key within one process (take(key, n)).",
          "You want to return, when it stops a call, the shortest number of milliseconds (retry_after_ms) after which waiting lets the call through exactly. One ms earlier, it does not get through.",
          "You want to see what remains without reducing the bucket (peek does not create a new key); you want to forget a key and return it to full (reset); or you want to forget only full buckets (prune).",
          "You want to inject a clock to make results deterministic in tests. If the sequence of values from the injected clock is the same, the same sequence of operations gives the same results.",
          "You need a way of calling it that does not throw. The factory and the instance methods do not throw exceptions."
        ],
        "do_not_use_when": [
          "You need rate limiting whose counts are shared across multiple processes or machines. The state is per instance and only in this process's memory, and is not shared with other processes, instances or machines. It is not distributed rate limiting.",
          "You want the counts to survive a restart. When the process restarts, the counts are lost and every key returns to full.",
          "You want to query or change a provider's quotas or limits. That is an outside boundary and the role of a separate Tsugite.",
          "You need counting by time window (fixed window or sliding window) or a different limit for each key.",
          "You want to share one instance between threads (worker_threads). It assumes use within one event loop.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When there are not enough tokens, it returns RATE_LIMITED with remaining and retry_after_ms, and does not change the bucket.",
          "It stops with INVALID_OPTIONS when options is not an object or reading it raises an exception, with UNKNOWN_OPTION for an unknown option, and with INVALID_OPTION (with the name in field) when a type or range is wrong. It does not silently ignore a misspelling.",
          "When the time from empty to full exceeds a safe integer number of milliseconds, it cannot return retry_after_ms exactly, so it stops with REFILL_TOO_SLOW.",
          "It stops with INVALID_KEY when key is not a string of 1 to 256 characters, with INVALID_AMOUNT when n is not a safe integer of 1 or more, and with REQUEST_TOO_LARGE when n exceeds capacity.",
          "When a new key would exceed max_keys, it stops with TOO_MANY_KEYS without creating state.",
          "It stops with CLOCK_WENT_BACKWARDS when the clock goes back before the newest time it has received, with CLOCK_FAILED when clock.now() throws, and with INVALID_CLOCK_VALUE when it does not return a safe integer."
        ],
        "what_it_does_not_do": [
          "It does not share counts across multiple processes or machines. The state is only in this process's memory and is lost on restart. Distributed rate limiting (which needs shared storage) is not this part.",
          "Of the \"quota\" in its name, it does not query or change a provider's quotas or limits. That is an outside boundary and the role of a separate Tsugite. This part only does the counting inside the process.",
          "It has not been checked that it matches how a provider's API counts quotas or rate limits.",
          "It cannot detect a clock that has run ahead (a future value). The monotonicity of the default clock (process.hrtime.bigint) is left to Node.js.",
          "It does not decide the business meaning of who is allowed how much. That is the role of the Kernel and the caller.",
          "Letting a call through is neither permission to make that call nor proof that quota remains on the provider's side (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular rate-limiting library or service."
        ],
        "integration": "Call createRateLimiter(options) in src/rate-limit.js (it does not throw). options is only { capacity, refill_tokens, refill_interval_ms, max_keys, clock }, and clock is optional. Returns: { ok, errors, instance }. The instance's take(key, n) returns { ok, errors, remaining, retry_after_ms }, and peek, reset and prune return remaining, removed and keys. When it stops, the code in errors is from error_taxonomy. Side effect: NONE. There is no outside connection, no credential and no outside interface for the caller to supply; the state is in the process's memory, and the only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-02",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Circuit breaker — Circuit breaker part (Primitive)",
        "summary": "The Circuit breaker part (Primitive) is a standalone part that counts failures in a window of the most recent calls and moves CLOSED → OPEN → HALF_OPEN → CLOSED or OPEN. The caller asks allow() before calling and passes the result with record({ outcome }). Whether something is a failure is decided by the caller. A result with no matching allow is stopped with UNKNOWN_TRIAL and not counted (fail-closed). The state is per instance and only in this process's memory.",
        "problem": "When temporarily stopping calls to a counterpart that keeps failing, within one process, it prevents HALF_OPEN from closing on its own because of a late result from an earlier call or a trial from an earlier round, and prevents the state from changing when it stops a call.",
        "use_when": [
          "You want to temporarily stop calls to a counterpart that keeps failing, within one process. The caller asks allow() before calling and passes the result with record.",
          "You want to go OPEN based on the number of FAILURE results (failure_threshold or more) among the most recent window_size results.",
          "You want to return, while OPEN, the exact number of milliseconds until the next trial is possible (retry_after_ms).",
          "You want to tie HALF_OPEN trials together with trial_id so that you never mistake which result belongs to which trial.",
          "You want to inject a clock to make results deterministic in tests. It uses no random numbers (trial_id is a sequence number).",
          "You need a way of calling it that does not throw. The factory and the instance methods do not throw exceptions."
        ],
        "do_not_use_when": [
          "You need a breaker whose state is shared across multiple processes or machines. The state is per instance and only in this process's memory, and is not shared with other processes, instances or machines.",
          "You want to keep OPEN across a restart. When the process restarts, it returns to CLOSED with an empty window and counts again, even if the counterpart is still down.",
          "You want to count by time window (the last N seconds), by failure rate or by slow calls. It has a count-based window only.",
          "You want HALF_OPEN to wait for several successful trials before closing. It decides by the result of the first trial that arrives.",
          "You cannot always record the result of a trial. Trials have no deadline, and if no result arrives it stays HALF_OPEN_BUSY and does not issue the next trial.",
          "You want to share one instance between threads (worker_threads), or you use it on a version other than Node.js v24.21.0 (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "While OPEN, allow() returns CIRCUIT_OPEN and retry_after_ms. One ms earlier, it does not issue a trial.",
          "For a result with no matching allow (no trial_id in HALF_OPEN or OPEN, an unknown trial_id, the trial_id of a trial from an earlier round or of one that has become invalid, or a trial_id in CLOSED), it stops with UNKNOWN_TRIAL without counting it and does not change the state.",
          "When this round has issued half_open_max_trials trials and their results have not yet arrived, it stops with HALF_OPEN_BUSY.",
          "It stops with INVALID_RECORD when the argument to record is not an object with only { outcome, trial_id? } or trial_id is not a string, and with INVALID_OUTCOME when outcome is not \"SUCCESS\" or \"FAILURE\" (case-sensitive).",
          "It stops with INVALID_OPTIONS when options is not an object, with UNKNOWN_OPTION for an unknown option, and with INVALID_OPTION when a type or range is wrong (including failure_threshold exceeding window_size).",
          "It stops with CLOCK_WENT_BACKWARDS when the clock goes back before the newest time it has received, with CLOCK_FAILED when clock.now() throws, and with INVALID_CLOCK_VALUE when it does not return a safe integer."
        ],
        "what_it_does_not_do": [
          "It does not share state across multiple processes or machines. The state is only in this process's memory and is lost on restart. A shared breaker is not this part.",
          "Of \"stopping cascading failures\" in its name, it does not decide which results count as failures (classification by looking at exceptions or responses). It only counts the SUCCESS or FAILURE that the caller decided.",
          "It does not decide what to do when it stops a call (an alternative candidate from F-07, a retry from F-03 and so on), and it does not make the call itself. That is the caller's role.",
          "state() does not read the clock. Even after open_ms has passed, it returns OPEN until allow() is called.",
          "It cannot detect a clock that has run ahead (a future value). The monotonicity of the default clock (process.hrtime.bigint) is left to Node.js. It does not claim that its transitions or defaults are the same as those of other circuit breaker libraries.",
          "allow() letting a call through is neither permission to make that call nor proof that the counterpart has recovered (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular circuit breaker library."
        ],
        "integration": "Call createCircuitBreaker(options) in src/circuit-breaker.js (it does not throw). options is only failure_threshold, window_size, open_ms, half_open_max_trials and clock (optional). On the returned instance, allow() returns state, trial_id? and retry_after_ms?; record({ outcome, trial_id? }) returns state; and state() returns the counts in the window, opened_at and so on. Every result has { ok, errors }, and when it stops, the code in errors is from error_taxonomy. Side effect: NONE. There is no outside connection, no credential and no outside interface for the caller to supply; the state is in the process's memory.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-03",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Retry (exponential backoff) — Retry (exponential backoff) part (Primitive)",
        "summary": "The Retry (exponential backoff) part (Primitive) is a standalone part that counts the retries of one operation and decides the time of the next attempt. It computes the delay as capped exponential backoff in integer ms and, with jitter \"full\", chooses it with an injected random source. It does not run the operation and does not wait. A retry of an operation that is not idempotent (idempotent: false) is stopped with NOT_IDEMPOTENT (fail-closed). The state is per instance and only in this process's memory.",
        "problem": "When retrying a transient failure that \"sometimes fixes itself\", it prevents retrying an operation that is not idempotent, letting a forgotten idempotent setting through, retrying without end or overflowing the delay, and silently ignoring unknown keys.",
        "use_when": [
          "You want to manage, within one process, the number of attempts at one operation (max_attempts, including the first attempt) and the time of the next attempt (not_before_ms).",
          "You want to compute the capped exponential backoff delay min(max_ms, base_ms × multiplier^(attempt-1)) in integer ms (computeDelay is a pure function with no state).",
          "You want jitter \"full\" to choose an integer delay in [0, delay] with an injected random source.",
          "You want to allow only the first attempt of an operation that is not idempotent and stop its retries (idempotent: false).",
          "You want to inject a clock and a random source to reproduce results in tests.",
          "You need a way of calling it that does not throw. The factory, computeDelay and the instance operations do not throw exceptions."
        ],
        "do_not_use_when": [
          "You want to count the total retries across multiple processes or instances. The state is per instance and only in this process's memory, and is not shared across processes or instances.",
          "You want to keep the number of attempts across a restart. The state is lost when the process ends.",
          "You want the part to run the operation or to wait (with a timer) until not_before_ms. Both are the caller's role.",
          "You need Equal Jitter, Decorrelated Jitter or a multiplier that is not an integer (such as 1.5). jitter is none or full only, and multiplier is integers only.",
          "You need unpredictable random quality. For the injected random source, it only checks that the return value is an integer in range.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "After RETRYABLE_FAILURE is recorded for an operation with idempotent: false, the run becomes HALTED and the next attempt is stopped with NOT_IDEMPOTENT (even if attempts remain). When idempotent is missing or not a boolean, it stops with INVALID_IDEMPOTENT (it sets no default).",
          "It stops with TOO_EARLY (with not_before_ms and retry_after_ms) for an attempt before not_before_ms, and with RETRIES_EXHAUSTED when the number of attempts has reached max_attempts.",
          "It stops when attempts and records do not alternate (nextAttempt during an attempt gives ATTEMPT_IN_FLIGHT; record with no attempt gives NO_ATTEMPT_IN_FLIGHT), and stops an attempt or record after the run has finished with RUN_FINISHED.",
          "When the input to record is not only { outcome }, or outcome is not SUCCESS, RETRYABLE_FAILURE or FINAL_FAILURE, it stops with INVALID_OUTCOME.",
          "It stops with INVALID_OPTIONS when options or policy is not a plain object, with UNKNOWN_OPTION (with key) for an unknown key, and with the corresponding INVALID_* code when attempt, base_ms, max_ms, multiplier, jitter or max_attempts is out of range.",
          "It stops with INVALID_RANDOM when random is not a function, and with RANDOM_OUT_OF_RANGE when it does not return an integer in [0, delay] or throws.",
          "It stops with INVALID_CLOCK when clock has no now(), with CLOCK_INVALID when clock.now() does not return a safe integer of 0 or more or throws, and with CLOCK_WENT_BACKWARDS when it goes back from the value read before.",
          "An operation that was stopped (ok: false) does not change the number of attempts, the stage or not_before_ms."
        ],
        "what_it_does_not_do": [
          "The state is only in this process's memory; it is not shared between processes or instances and is lost on restart. It cannot be used to count retries across multiple processes.",
          "Of \"retrying transient failures\" in its name, it does not carry out the retry. Running the operation and waiting (with a timer) until not_before_ms are the caller's.",
          "It does not decide whether a failure is transient (RETRYABLE or FINAL) or whether a retry is acceptable for the business. It uses idempotent and outcome exactly as the caller declares them and does not check whether they are correct.",
          "It does not read the counterpart's Retry-After or rate-limit responses (that is on the Tsugite side). Limiting the overall waiting time is the role of F-06, and stopping the call itself is the role of F-02.",
          "For clock monotonicity it only stops when the clock goes back; keeping the clock monotonic is up to whoever injects it. The fuzz reference model was written separately from the same specification by the same author as the implementation, so a misreading of the specification present in both would not be found.",
          "Even if nextAttempt is ok, that is not permission to run the operation. retry_allowed: true is not a judgement that a retry should be made for the business (result_is_approval: false, derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular retry library."
        ],
        "integration": "Call createRetryRun(options) in src/retry.js (a factory that does not throw). options is only max_attempts, base_ms, max_ms, multiplier, jitter, idempotent (required), clock and random. On the returned instance, nextAttempt() returns attempt and not_before_ms, record({ outcome }) returns state and retry_allowed, and snapshot() returns the state. computeDelay(policy, random) is a pure function that returns delay_ms. Every result has { ok, errors }, and when it stops, the code is from error_taxonomy. Side effect: NONE. There is no outside connection, no credential and no outside interface for the caller to supply; the state is in the process's memory.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-04",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Idempotency key handling — Idempotency key handling part (Primitive)",
        "summary": "The Idempotency key handling part (Primitive) is a standalone part that compares a new request (stage, op_id and the content digest) against a given ledger (requests received before and the digests of their results) and sorts it into one of: first time (NEW), a resend of the same request with a result (DUPLICATE_SAME), a resend of the same request with no result yet (DUPLICATE_PENDING), or different content under the same ID (CONFLICT). The stages are REQUEST, ATTEMPT and DELIVERY, and each stage is counted separately. Input it cannot accept is stopped with a fixed code and position (fail-closed).",
        "problem": "This identification is there to keep a resend from running the same processing twice. It prevents passing a request with the same ID but different content as a resend of the same request, treating a resend whose result is unknown as a new run, and silently skipping ledger rows of the wrong shape.",
        "use_when": [
          "You want to identify whether a new request is a resend of a request in the given ledger, by exact match of the idempotency key (stage, op_id) and the content digest (payload_digest) (classify).",
          "You want to return the earlier result digest for a resend of the same request. The result_digest of DUPLICATE_SAME is exactly the result_digest of that ledger row.",
          "You want a resend to a row that has no result yet (result_digest is null) to come back as DUPLICATE_PENDING, a marker not to run it again. It is not a decision to run it again; it is a marker for waiting for reconciliation.",
          "You want to identify a request with the same ID but different content as CONFLICT. This differs from identification that decides duplicates by ID alone, without looking at differences in content under the same ID.",
          "You want request receipt (REQUEST), attempts (ATTEMPT) and delivery (DELIVERY) counted separately by stage. Across different stages, the same op_id is not compared. counts returns the number of distinct op_ids per stage.",
          "You want to call it locally, as processing that does not throw an exception, does not modify its input, and has no state. The same input always gives the same answer."
        ],
        "do_not_use_when": [
          "You want to store or read the ledger, or find duplicates against other calls or a stored ledger. It compares only within its arguments; storing and reading the ledger is done outside this part, on the side of the Host and so on.",
          "You want to settle a race between two requests that arrive at the same time. Which goes first is decided by the side that writes the ledger (the Host).",
          "You want to reject, record or report a request, or carry out a resend. CONFLICT only returns a marker for a request to be rejected; rejecting, recording and reporting are the caller's work. It does not carry out resends, keep a clock or store anything either.",
          "You want op_ids that look the same (é and e plus a combining character, A and a, differences in leading or trailing whitespace) to be treated as the same. op_id is compared only by exact match of UTF-16 code units.",
          "You want to pass a digest in upper case or with a sha256: prefix. The digest is not converted; anything other than 64 lower-case hexadecimal digits stops with INVALID_DIGEST. The rules for making a digest from content (I-08 canonicalization, B-07 hashing and so on) are decided by the caller, and both the ledger and the request are made by the same rules.",
          "You want to handle a ledger of more than 100,000 rows in one call. The caller splits it. Once split, duplicates against rows in the other parts are not identified.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When request is not a plain object (including inherited ones, class instances and a null prototype), or stage, op_id or payload_digest is not an own data property (missing or a getter), it stops with MALFORMED_REQUEST.",
          "When ledger is not a plain array it stops with INVALID_LEDGER, when it has more than 100,000 rows with TOO_MANY_ENTRIES, and when a row is not a plain object (including holes and getter rows) or its four fields are not own data properties with MALFORMED_ENTRY.",
          "When stage is none of REQUEST, ATTEMPT and DELIVERY (including lower case and boxed strings) it stops with INVALID_STAGE, when op_id is not a string or is empty with INVALID_OP_ID, and when it exceeds 256 UTF-16 code units with OP_ID_TOO_LONG.",
          "When payload_digest is not a string of 64 lower-case hexadecimal digits (upper case, a prefix, the wrong number of digits, non-hexadecimal characters, a boxed string, an array, null), or result_digest is neither a string of that form nor null, it stops with INVALID_DIGEST.",
          "When the ledger has two rows with the same (stage, op_id), it stops with DUPLICATE_LEDGER_ENTRY (at the position of the second one), even if the content is the same. When inspecting a value itself raises an exception (a Proxy trap and so on), it is INSPECTION_FAILED.",
          "Names such as __proto__ and constructor are also compared as ordinary op_ids (compared with a Map, not used as object property names). Getters are not called.",
          "When it stops, it returns the code and position (path, a JSON Pointer) of the first problem found in a fixed order, and returns neither decision nor the other fields.",
          "Fuzzing (1,000,000 runs) that compares the results against an independent oracle written to search the ledger from the front in order, without using Map or Set, confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "NEW only means \"not in the given ledger\"; it does not mean that the request may be run. Duplicates against a ledger that was not passed in (one held elsewhere, or not yet stored) are not known.",
          "It does not look at whether the content digest was really made from the content (it only compares the digests passed in). It does not look at whether the result digest of DUPLICATE_SAME is correct either.",
          "Accepting exactly the limit of 100,000 rows was checked only by unit tests (the fuzzing covers only inputs over the limit).",
          "It has no external boundary, no state, no clock and no random numbers, and stores nothing. Side effect: NONE, and it uses no credentials.",
          "The result of the identification is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The input and output forms were defined in v1; there is no existing implementation (new).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular idempotency key library."
        ],
        "integration": "Call classify(request, ledger) in src/idempotency-ledger.js (it does not throw and does not modify its input). Input: a plain object { stage, op_id, payload_digest } and a plain array of rows { stage, op_id, payload_digest, result_digest } (up to 100,000 rows). Returns: { ok: true, errors: [], decision, matched_index, result_digest, counts } or { ok: false, errors: [{ code, path }] }. There is no side effect, no state, no external connection and no credential; the only dependencies are Node.js built-ins, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-05",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Concurrency limit (bulkhead) — Concurrency limit (bulkhead) part (Primitive)",
        "summary": "The Concurrency limit (bulkhead) part (Primitive) is a standalone part that limits, per compartment, how many run at the same time (max_concurrent) and the length of the waiting queue (max_queue). If a slot is available it hands out a token; if the slots are full but the queue has room it hands out a ticket; if both are full it stops with BULKHEAD_FULL (fail-closed). The limits are per instance and only in this process's memory; it is not a distributed limit.",
        "problem": "When dividing work into compartments within one process so that runaway work in one part does not drag down other work in the same process, it prevents handing out slots beyond the limit, and opening extra slots through a double release or a fake token.",
        "use_when": [
          "You want to limit, within one process, how many run at the same time and the length of the waiting queue for each compartment divided by name. Compartments are independent; one compartment being full does not affect the others.",
          "When the slots are full, you want to put work in a waiting queue and promote it in FIFO order. The promoted token goes into promoted in the result of release, and the waiting side learns of it with poll.",
          "You have a compartment that should refuse at once without waiting (max_queue 0).",
          "You want to remove work from the waiting queue (cancel). A cancelled ticket is not promoted.",
          "You need a way of calling it that does not throw. The factory and the instance operations do not throw exceptions."
        ],
        "do_not_use_when": [
          "You need a concurrency limit across multiple processes or machines (a distributed semaphore or shared counting). The state is per instance and only in this process's memory, and is not shared across processes or instances.",
          "You want the slot counts to survive a restart. The state is lost on restart.",
          "You need a waiting deadline (timeout). Stopping excessive waits is for F-06 or the caller.",
          "You want to wake the waiting side with a notification or a Promise. This part does not wake the waiting side.",
          "You want the slot of a token whose release was forgotten to be reclaimed automatically. There is no reclamation by deadline and no leak detection.",
          "You need priorities, weights or fairness adjustment. Inside a compartment it is FIFO only.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When both the compartment's slots and its waiting queue are full, it stops with BULKHEAD_FULL (with the name in compartment). It does not hand out slots beyond the limit.",
          "It stops with UNKNOWN_TOKEN when the token is not the very one this instance handed out (a fake of the same shape, one from another instance, or a ticket mistaken for a token), and with TOKEN_ALREADY_RELEASED for a token already released, and does not open a slot.",
          "It stops with UNKNOWN_TICKET for a ticket this instance did not hand out, with TICKET_CANCELLED for one already cancelled, and with TICKET_ALREADY_GRANTED for a cancel after promotion.",
          "When the compartment name given to acquire is not among the compartments set at creation (including prototype names such as constructor and toString), it stops with UNKNOWN_COMPARTMENT.",
          "It stops with INVALID_OPTIONS when options is not a plain object, with UNKNOWN_OPTION for an unknown key, with INVALID_COMPARTMENTS when there are 0 or 257 or more compartments, and with the corresponding INVALID_* code when the shape or range of a compartment name, a compartment value, max_concurrent or max_queue is wrong.",
          "It stops with INVALID_CLOCK when clock has no now(), with CLOCK_INVALID when clock.now() does not return a safe integer of 0 or more or throws, and with CLOCK_WENT_BACKWARDS when it goes back from the value read before.",
          "An operation that was stopped (ok: false) does not change the slots, the waiting queue, tokens or tickets."
        ],
        "what_it_does_not_do": [
          "It does not limit the total across multiple processes or machines. The state is only in this process's memory and is lost on restart. A distributed limit has an outside boundary, so it belongs on the Tsugite side, not in this part.",
          "Of \"isolating runaway parts\" in its name, it does not detect or stop runaway work. It only limits, per compartment, how many run at the same time and the length of the waiting queue.",
          "It has no waiting deadline, no way of waking the waiting side, and no reclamation of slots whose release was forgotten.",
          "A token is a handle within the same process; it is not proof of ownership, authority or permission to run. It does not stop the receiver from passing it on to others (result_is_approval: false, derives_authority: false).",
          "The fuzz reference model was written separately from the same specification by the same author as the implementation, so a misreading of the specification present in both would not be found.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular semaphore or pool library."
        ],
        "integration": "Call createBulkhead({ compartments, clock? }) in src/bulkhead.js (a factory that does not throw). On the returned instance, acquire(name) returns a token (queued: false) or a ticket (queued: true), release(token) returns compartment and promoted, poll(ticket) returns granted and a token, cancel(ticket) returns compartment, and snapshot() returns active and queued for each compartment. Every result has { ok, errors }, and when it stops, the code in errors is from error_taxonomy. Side effect: NONE. There is no outside connection, no credential and no outside interface for the caller to supply; the state is in the process's memory.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-06",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Timeouts and deadline propagation — Timeout and deadline propagation part (Primitive)",
        "summary": "The Timeout and deadline propagation part (Primitive) is a standalone part that builds absolute deadlines on an injected monotonic clock (clock.now(), in milliseconds) and passes deadlines and cancellation from parent to child. A child's deadline never extends beyond its parent's. Cancellation is a mark within this process and does not mean that outside processing has stopped. Values it cannot accept and a clock that goes back are stopped with fixed codes (fail-closed). The state is only in this process's memory.",
        "problem": "When calls are nested within one process, it prevents a child's deadline from extending beyond its parent's, an expiry from being undone because the clock went back, and cancellation from failing to reach descendants, and it makes excessive waiting (hangs) readable.",
        "use_when": [
          "You want to create a deadline for processing within one process and pass the remaining time (remaining_ms) on to what you call. It is a candidate to become the common part that passes the remaining time to Tsugite that call outside APIs.",
          "You want a child's deadline to be min(the parent's deadline, now + its own timeout) so that propagation never widens the deadline (child).",
          "You want to mark itself and all its descendants as cancelled (cancel). cancel does not read the clock, so cancellation works even if the clock is broken.",
          "You want to read the deadline's state (ACTIVE, EXPIRED or CANCELLED) and the cancellation's reason and origin (SELF or ANCESTOR) (status, expired).",
          "You want to inject a clock to make results deterministic in tests. It uses no wall clock (Date), no timers and no random numbers.",
          "You need a way of calling it that does not throw. Neither the factory nor the instance methods throw exceptions."
        ],
        "do_not_use_when": [
          "You need deadline propagation across processes or in a distributed way (sending it in headers and so on). The state is only in this process's memory, with no sharing between processes, no distributed sharing and no persistence.",
          "You want deadlines to survive a restart. When the process ends, the state is lost.",
          "You want to cancel outside processing (a counterpart's API and so on). Cancellation is only a mark within this process; outside cancellation is done by the caller or a Tsugite under its own contract.",
          "You need a notification (timer) at the deadline time. The caller only finds out when it reads.",
          "You hold one tree for a long time and create more than 10,000 deadlines in it. There is no operation to release a deadline, and finished deadlines stay in memory and count toward the number.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When a clock reading is smaller than the largest value read in that tree, it stops with CLOCK_WENT_BACKWARDS without changing its records. A deadline that has once become EXPIRED never returns to ACTIVE.",
          "When you try to create a child of an expired or cancelled deadline, it stops with DEADLINE_ALREADY_OVER.",
          "When timeout_ms is not a safe integer from 1 to 2,147,483,647 (0, negative, fractional, NaN, a string or above the upper limit), it stops with INVALID_TIMEOUT.",
          "It stops with INVALID_CLOCK when clock has no now(), with INVALID_CLOCK_READING when clock.now() throws or does not return a finite number from 0 to MAX_CLOCK_MS, and with CLOCK_MISMATCH when a clock different from the one of the parent's tree is passed.",
          "When parent is not a deadline instance created by this part (including copies), it stops with INVALID_PARENT.",
          "It stops with INVALID_OPTIONS when the options are not an object, have a key that is not allowed or throw when read, and with INVALID_REASON when reason_code is not 1 to 64 characters of uppercase letters, digits and _ starting with an uppercase letter.",
          "When the number of deadlines in a tree reaches 10,000 (MAX_DEADLINES_PER_TREE), it stops with TOO_MANY_DEADLINES."
        ],
        "what_it_does_not_do": [
          "The state is only in this process's memory; it is not shared between processes or in a distributed way, and it is lost when the process ends. The \"deadline propagation\" in its name is only propagation from parent to child within this process; it does not propagate across processes (sending it in headers and so on).",
          "Of the \"timeout\" in its name, it does not stop processing. Cancellation is a mark within this process (local_only: true), and it does not handle cancelling outside processing or whether that succeeds. Outside cancellation is on the side of each Tsugite and its contract.",
          "It does not notify at the deadline time (it has no timers).",
          "It cannot check whether the injected clock is truly monotonic (it only stops readings that go back). The monotonicity of the default clock depends on Node.js's process.hrtime.bigint. Compatibility with the deadline shape of any Tsugite or library has not been checked.",
          "Remaining time, expiry and cancellation are only facts on this part's clock and marks; they do not mean that outside processing has stopped, has finished or may be redone (result_is_approval: false, derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular library (AbortController and so on)."
        ],
        "integration": "Call createDeadline({ timeout_ms, clock?, parent? }) in src/deadline.js (a factory that does not throw). On the returned instance, remaining() returns remaining_ms and state, expired() returns expired, cancelled and over, child({ timeout_ms }) returns a child instance, cancel({ reason_code }) returns newly_cancelled and local_only: true, and status() returns state, deadline_at and cancelled. Every result has { ok, errors }, and when it stops, the code is from error_taxonomy. Side effect: NONE. There is no outside connection, no credential and no outside interface for the caller to supply; the state is in the process's memory.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "F-07",
        "category": "F/耐障害・流量制御",
        "kind": "Primitive",
        "title": "Fallback — Fallback part (Primitive)",
        "summary": "The Fallback part (Primitive) is a standalone part that decides, one step at a time, whether it may move on to the next candidate after a failure, strictly following an \"ordered allow-list\" decided outside it (by policy, the Owner or the caller). It does not add candidates, reorder them, skip them or widen what is allowed. It does not call candidates; it only returns an id. An allow-list or report of the wrong shape is stopped with a fixed code (fail-closed). The state covers one run and is only in this process's memory.",
        "problem": "When switching to the next candidate so that things \"still work, partially\", it prevents moving to a candidate that was not decided, skipping in the order, switching after a failure that is not allowed, trying again with a different code after being refused, and the allow-list being rewritten after creation.",
        "use_when": [
          "You want switches of an AI provider or model, a storage destination and so on to happen only in the order decided outside the part.",
          "You want to decide, per failure code, whether moving to the next candidate is allowed. next returns the next candidate's id only when the one that failed is the current candidate and the code is in the allowed_after of the immediately following candidate.",
          "You want an upper limit on the number of switching steps (max_steps, 0 to 31).",
          "You need a deterministic decision that uses no clock, no random numbers and no environment.",
          "You need a way of calling it that does not throw. Neither the factory nor the instance methods throw exceptions."
        ],
        "do_not_use_when": [
          "You want to share a run's state across multiple processes or machines. The state covers one run and is only in the instance in this process's memory, with no sharing between processes and no persistence.",
          "You want to keep the current candidate and step count across a restart. When the process ends, the state is lost.",
          "You want the part to decide which candidates to list or after which failures switching is allowed. That is decided outside it (by policy, the Owner or the caller).",
          "You want it to handle calling the candidates, waiting time, retries or circuit breaking as well. Deadlines are F-06, retries F-03 and circuit breaking F-02.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When error_code is not in the allowed_after of the immediately following candidate, it stops with FALLBACK_NOT_ALLOWED and ends that run. After being refused, it cannot be tried again with a different code.",
          "When failed_id is not the current candidate (an earlier candidate, a later candidate or an unknown id), it stops with OUT_OF_ORDER without changing the state.",
          "It stops with NO_MORE_CANDIDATES when the current candidate is the last, and with MAX_STEPS_REACHED when the step count has reached max_steps, and ends that run. next after the run has ended gives RUN_ENDED.",
          "It stops with ALREADY_STARTED when first is called twice, with NOT_STARTED when next is called before first, and with INVALID_REQUEST when the report to next is not an object with only failed_id and error_code.",
          "It stops with INVALID_CANDIDATES when candidates is not an array, is empty or has more than 32 entries, with INVALID_CANDIDATE_ID when an id has the wrong shape, and with DUPLICATE_CANDIDATE_ID when the same id appears twice.",
          "It stops with INVALID_ALLOWED_AFTER when allowed_after has the wrong shape (a code of the wrong shape, duplicate codes, more than 64, not empty for the first candidate, or empty for a candidate from the second on), with INVALID_MAX_STEPS when max_steps is not a safe integer from 0 to 31, and with INVALID_OPTIONS when the options have the wrong shape.",
          "Even if the caller rewrites the allow-list array after creation, the results do not change (it keeps a copy)."
        ],
        "what_it_does_not_do": [
          "The state covers one run and is only in this process's memory; it is not shared between processes or machines and is lost when the process ends. It does not handle run state across processes or distributed run state.",
          "Of \"degraded operation\" in its name, it does not perform the degraded behaviour itself. It does not choose, switch or call a provider or model; it only returns the next id in the order decided outside it. Calling a candidate is done by the caller and each Tsugite.",
          "It does not judge whether the allow-list's contents are correct (whether switching to that candidate is acceptable, or whether the cost or the scope of the data handled is the same). It only keeps to what was decided outside it.",
          "It cannot check whether a failure code is genuine. It does not interpret what a code means either; it only looks for an exact string match.",
          "The next candidate's id only means \"moving on is allowed on the allow-list decided outside\"; it is not authority to call that candidate, approval of cost or correctness of the result (result_is_approval: false, derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "Compatibility with the fallback shape of any Tsugite or library has not been checked."
        ],
        "integration": "Call createFallback({ candidates, max_steps }) in src/fallback.js (a factory that does not throw). candidates is an ordered list of { id, allowed_after } (1 to 32 entries). On the returned instance, first() returns the first candidate { id }, next({ failed_id, error_code }) returns the next candidate { id }, index, step and allowed_by, and status() returns state (READY, ACTIVE or ENDED), current, steps and end_code. When it stops, the code in errors is from error_taxonomy. Side effect: NONE. There is no clock, no outside connection, no credential and no outside interface for the caller to supply; calling the candidates is the caller's job. It is pure JS with no dependencies.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-01",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Payment authorization — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks a payment authorization request (PaymentAuthRequest) against the pinned payment methods and the setting that allows operations that move money (allow_money_movement), carries it to the payment platform, and matches the returned receipt (PaymentAuthReceipt) against the contract. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When a payment authorization is carried to an external payment platform, this Tsugite prevents sending an operation that moves money when that has not been allowed, using a payment method that is not pinned, and trusting a receipt that leaves it unknown whether money moved or a receipt that claims business approval. It is a check against mistaking a successful payment for business approval.",
        "use_when": [
          "You want to carry a payment authorization (AUTHORIZE) request to an external payment platform and treat the returned receipt as a result only after matching that its state fits op (AUTHORIZED).",
          "You want operations that move money to be sent only when it has been explicitly allowed by the setting allow_money_movement: true (by default nothing is sent).",
          "You want to limit the payment methods that may be used to a pinned list (method_refs, references that are not card numbers).",
          "You do not want the same authorization to run twice. An idempotency key is required, the same request_id is not sent twice, and a resend after a failure is resolved with a lookup (lookup)."
        ],
        "do_not_use_when": [
          "You want to treat a successful payment as business approval. A successful payment is not business approval (business_approval is not derived from carrying).",
          "You want to confirm that amounts, currencies, taxes or fees are correct. This Tsugite does not check them (that is the role of external decisions and of people and organizations).",
          "You want to keep the records of authorizations after a restart. The records exist only inside this Tsugite and are lost on restart (persistence will be settled at a separate gate when connecting).",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, it does not send operations that move money (REFUSED).",
          "It checks the pinned lists. It does not send a payment method that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "It checks the idempotency key. It does not send an authorization without an idempotency key, or a request that reuses a key with different content.",
          "It checks the authorization records. It does not send a capture of a payment that this Tsugite has not matched as AUTHORIZED, a capture after a void, or a second capture.",
          "It matches the receipt. A receipt whose state does not match op, a receipt that claims an effect for a decline, a receipt that increases the external effect, or a receipt for another request (a spoofed request_digest) is not accepted, and the client stops from then on (HALTED).",
          "A receipt that leaves it unknown whether money moved (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED). The case is passed to a person for review.",
          "A receipt that claims business approval is not accepted.",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "A successful payment is not business approval. Every result carries approval: false. Business approval is the role of the Kernel (approval-facts) and of people and organizations.",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not check whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass a PaymentAuthRequest to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries op, auth_state and business_approval in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for authorization operations; NONE for declines, pending results, failures and similar.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-02",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Payment capture and void — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) carries a payment capture (CAPTURE) and a void (VOID) as a payment authorization request (PaymentAuthRequest). For capture and void, it sends only payments that this Tsugite has matched as AUTHORIZED and that have not yet been captured or voided, and it matches the returned receipt (PaymentAuthReceipt) against the contract. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When an authorized payment is captured or voided, this Tsugite prevents capturing a payment that has not been authorized, capturing a payment that has been voided, capturing the same payment twice, and trusting a receipt that leaves it unknown whether money moved or a receipt that claims business approval. It is a check against mistaking a successful capture for business approval.",
        "use_when": [
          "You want to carry a payment authorized through this Tsugite (already matched as AUTHORIZED) to an external payment platform as a capture (CAPTURE to CAPTURED) or a void (VOID to VOIDED), and treat the returned receipt as a result only after matching that its state fits op.",
          "You want a capture to be sent only when it has been explicitly allowed by the setting allow_money_movement: true (by default nothing is sent).",
          "After a capture or void, you want to prevent the same payment from being captured a second time.",
          "When resending after a payment platform failure, you want to resolve it with a lookup (lookup) so that nothing runs twice."
        ],
        "do_not_use_when": [
          "You want to capture or void a payment that was authorized through another route. Capture and void apply only to payments that this Tsugite has matched as AUTHORIZED.",
          "You want to keep the records of authorizations after a restart. The records exist only inside this Tsugite and are lost on restart (persistence will be settled at a separate gate when connecting).",
          "You want to treat a successful capture as business approval. A successful payment is not business approval (business_approval is not derived from carrying).",
          "You want to confirm that amounts, currencies, taxes or fees are correct. This Tsugite does not check them (that is the role of external decisions and of people and organizations).",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the authorization records. It does not send a capture or void of a payment that this Tsugite has not matched as AUTHORIZED, a capture after a void, or a second capture (REFUSED).",
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, it does not send operations that move money, including a capture (REFUSED).",
          "It checks references. It does not send a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "It matches the receipt. A receipt whose state does not match op (anything other than CAPTURED for CAPTURE, or VOIDED for VOID), a receipt that claims an effect for a decline, a receipt that increases the external effect, or a receipt for another request (a spoofed request_digest) is not accepted, and the client stops from then on (HALTED).",
          "A receipt that leaves it unknown whether money moved (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED). The case is passed to a person for review.",
          "A receipt that claims business approval is not accepted.",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "A successful capture is not business approval. Every result carries approval: false. Business approval is the role of the Kernel (approval-facts) and of people and organizations.",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It keeps the records of authorized payments only inside this Tsugite (they are lost on restart). It does not persist them (persistence will be settled at a separate gate when connecting).",
          "It does not check whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass a PaymentAuthRequest whose op is CAPTURE or VOID to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries op, auth_state and business_approval in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for capture and void; NONE for declines, pending results, failures and similar.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-03",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Idempotent charging — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks an idempotent charge request (IdempotentChargeRequest) against the idempotency key, the pinned payment methods and the setting that allows operations that move money (allow_money_movement), carries it to the payment platform, and matches the returned receipt (IdempotentChargeReceipt) against the contract. The same idempotency key is treated as a duplicate, and no new effect is accepted (fail-closed).",
        "problem": "When a charge request is carried to an external payment platform, this Tsugite prevents charging twice through a resend or a duplicate, sending different content under the same key, and trusting a receipt that claims an effect for a duplicate. It is also a check against mistaking a successful charge for business approval.",
        "use_when": [
          "To prevent double charging, you want an idempotency key to be required on charge requests and a request with the same key to be treated as a duplicate (DEDUP_HIT, no new effect).",
          "You want charge requests to be sent only when it has been explicitly allowed by the setting allow_money_movement: true (by default nothing is sent).",
          "You want to match that the returned receipt is in a charge state that fits the request before treating it as a result (charge_state, charged).",
          "When resending after a payment platform failure, you want to resolve it with a lookup (lookup) so that nothing runs twice."
        ],
        "do_not_use_when": [
          "You want to treat a successful charge as business approval. A successful charge is not business approval (business_approval is not derived from carrying).",
          "You want to reconcile charges against deposits. Reconciliation is the role of external decisions and of people and organizations; this Tsugite does not reconcile (the paired Kernel is reconcile-facts).",
          "You want to confirm that amounts, currencies, taxes or fees are correct. This Tsugite does not check them.",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, it does not send operations that move money (REFUSED).",
          "It checks the idempotency key. It does not send a charge without an idempotency key, or a request that reuses a key with different content.",
          "A receipt that claims an effect for a duplicate is not accepted. The effect of a duplicate (DEDUP_HIT) is NONE.",
          "It checks the pinned lists. It does not send a payment method that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "It matches the receipt. A receipt whose state does not match op, a receipt that claims an effect for a decline, a receipt that increases the external effect, or a receipt for another request (a spoofed request_digest) is not accepted, and the client stops from then on (HALTED).",
          "A receipt that leaves it unknown whether money moved (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED). The case is passed to a person for review.",
          "A receipt that claims business approval is not accepted.",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "A successful charge is not business approval. Every result carries approval: false. Business approval is the role of the Kernel (approval-facts) and of people and organizations.",
          "It does not check whether amounts, currencies, taxes or fees are correct, and it does not reconcile statements (that is the role of external decisions and of people and organizations).",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass an IdempotentChargeRequest to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries charge_state, charged and business_approval in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for a charge; NONE for duplicates, declines, failures and similar.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-04",
        "category": "G/決済・資金",
        "kind": "Component",
        "title": "Invoice generation — Invoice generation part (Component)",
        "summary": "The Invoice generation part (Component) is a part that issues one invoice in stages: calculating the tax facts (the G-07 Kernel) → the document (PDF_RENDER of the J-01 Tsugite) → storage (the C-05 Tsugite, with the invoice number as the idempotency key) → the record (the audit ledger of the H-01 Tsugite). If G-07's facts show missing input or missing required entries, it does not make the document and returns only the facts. When a stage's result is unknown, it stops at that stage and continues from that stage with an issue call with the same number and the same content (fail-closed). The invoice number is decided by the caller and passed in.",
        "problem": "Behind accounting, it prevents an invoice with missing required entries or missing input from going on to the document, storage and the ledger, issuing an invoice with different content under the same number, storing twice, and passing the invoice's content to the ledger.",
        "use_when": [
          "You want to issue one invoice in the order calculation → document → storage → record, going on to the next stage only after the previous one has finished (issue).",
          "You want the tax facts produced by G-07's computeInvoiceTax and, if there is need_input or there are missing required entries, to receive them as they are with need_input and missing attached, without the document being made. The inputs invoice, tax_table and taxpayer_profile are G-07's input as is.",
          "You want the PDF stored only once with the C-05 Tsugite, with the invoice number as the idempotency key (invoice/<number>). It is stored only when the result is APPLIED or DEDUP_HIT.",
          "You want the fact of issuance recorded with the H-01 Tsugite as an AuditEventEnvelope in an append-only ledger. Only the digest is passed to the ledger, not the invoice's content.",
          "You want to resend a stage whose result was unknown, through an issue call with the same number and the same content, as the same request and the same envelope.",
          "You want to check the stage and digest for each invoice number (status)."
        ],
        "do_not_use_when": [
          "You want to use being issued (ISSUED) as approval of the billing, as sending to the recipient, as the establishment of a receivable, or as legal sufficiency of the required entries.",
          "You want to use being issued as a sign that the tax is correct for tax purposes. Being issued is not proof that it is correct for tax purposes. The tax facts remain those of G-07, and the source of truth for the facts is the tax rate table and the business's choices.",
          "You want it to assign invoice numbers or take care of their uniqueness or sequence. That is the caller's role.",
          "You also want it to handle sending to and receiving by the recipient, or the management of incoming payments and receivables. A Tsugite for sending and receiving external electronic invoices is not included in this part.",
          "You want to check the PDF layout, or that the required entries actually appear in the PDF. G-07's check of required entries is against what the input declares.",
          "You want to continue an unfinished stage across a restart. The state is only inside this process, and a restart loses the unfinished stage and the held request (storage does not happen twice thanks to the idempotency key, but the ledger envelope is made again).",
          "You need a part checked against real document generation, storage and ledger backends. It has never connected to a real backend.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, a reference is invalid or a port function is missing, it stops with INVALID_OPTIONS, when the input is malformed (missing or extra fields, the form of the invoice number and so on), with INVALID_INPUT, and when a getter throws an exception, with INSPECTION_FAILED.",
          "When G-07 does not return ok: true, it stops with TAX_INPUT_INVALID (with tax_errors attached), when G-07's function throws, with PORT_FAILED, and when the facts are malformed or inference or advice is not null, with TAX_FACTS_INVALID, and it does not make the document.",
          "When G-07's facts contain need_input, it stops with NEEDS_INPUT (it does not fill them in by guessing), and when required entries are missing, with REQUIRED_ITEMS_MISSING (it does not say how to fix them); it stops before the document is made and neither stores nor records.",
          "When the same invoice number comes with different content (any of the invoice, the tax rate table or the business's choices), it stops with INVOICE_NUMBER_REUSED. Another issue call for a number already issued returns already_issued: true.",
          "When the document Tsugite's result is unknown, it stops with RENDER_UNKNOWN, when the Tsugite did not send, with RENDER_REFUSED, and when it could not convert, converted only partly, halted or produced an empty PDF, with RENDER_FAILED, and it does not go on to storage.",
          "When the storage Tsugite's result is unknown, it stops with STORE_UNKNOWN (keeping the same request), when the Tsugite did not send, with STORE_REFUSED (including different content under the same idempotency key), and when it halted or the result is not APPLIED or DEDUP_HIT, with STORE_FAILED, and it does not go on to the record.",
          "When the ledger Tsugite's result is unknown (including when state() throws), it stops with LEDGER_UNKNOWN (keeping the same envelope), and when the ledger refuses, the head has advanced so the envelope no longer chains on, or the head is malformed, with LEDGER_REFUSED, and it does not mark the invoice as issued.",
          "When timestamp does not return a date-time string or throws, it stops with CLOCK_FAILED, and when the previous operation has not finished, with BUSY (only one operation at a time)."
        ],
        "what_it_does_not_do": [
          "The Invoice generation part is not a Tsugite. It has no Request or Receipt of its own; the external effects of converting the document, storing it and appending to the ledger are caused by the bundled J-01, C-05 and H-01 Tsugite under their own contracts. This part itself does not touch the outside.",
          "The meaning of the bundled parts (facts only; being converted is not a check of the content; being stored is not acceptance; being recorded is not approval) stays on each part's side.",
          "Being issued is not approval of the billing, the establishment of a receivable or legal sufficiency of the required entries (approval: false, claim: NOT_DERIVED, legal_sufficiency: NOT_VERIFIED, derives_authority: false).",
          "Being issued is not proof that it is correct for tax purposes. It makes no tax judgements, returns only facts and does not return how to fix anything (tax consultation is a service reserved exclusively for licensed tax accountants).",
          "It does not assign invoice numbers or take care of their uniqueness or sequence, and it does not handle sending to or receiving by the recipient, approval of the billing, or the management of incoming payments and receivables.",
          "It has never connected to real document generation, storage or ledger backends. The unit tests and fuzzing use stand-ins, and the wiring with the code of the real four parts was checked against each Tsugite's test stand-in.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use particular invoicing software."
        ],
        "integration": "Create it with createInvoiceIssuance(options) in src/invoice.js. Input: references, store_target, ledger, ports and an optional timestamp. Returns: { ok, errors, instance }; the results of issue and status carry approval: false, and an invoice that has gone on to a stage comes with stage and digest. User provides (injected through ports): taxFacts (G-07), render.transform (J-01), store.submit (C-05), and ledger.append and state (H-01). Side effect: caused by the bundled Tsugite (EXTERNAL_IRREVERSIBLE); credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-05",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Subscription and recurring billing — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks the billing (BILL_CYCLE), pause (PAUSE), resume (RESUME) and cancel (CANCEL) operations of a recurring billing request (SubscriptionBillingRequest) against the pinned lists and the setting that allows operations that move money (allow_money_movement), carries them to the payment platform, and matches the returned receipt (SubscriptionBillingReceipt). If a check fails, it does not send and does not accept (fail-closed).",
        "problem": "When recurring billing is carried to an external payment platform, this Tsugite prevents sending billing or resume operations that have not been allowed, sending the same billing twice, and trusting a receipt whose state does not match op or a receipt that leaves it unknown whether money moved.",
        "use_when": [
          "You want to carry recurring billing operations, billing (BILL_CYCLE to BILLED), pause (PAUSE to PAUSED), resume (RESUME to RESUMED) and cancel (CANCEL to CANCELLED), to an external payment platform and match that the receipt is in a state that fits op.",
          "You want operations that move money (billing and resume) to be sent only when it has been explicitly allowed by the setting allow_money_movement: true (by default nothing is sent).",
          "You want an idempotency key to be required on billing and a billing with the same key to be treated as a duplicate (DEDUP_HIT, no new effect).",
          "When resending after a payment platform failure, you want to resolve it with a lookup (lookup) so that nothing runs twice."
        ],
        "do_not_use_when": [
          "You want to treat a successful billing as business approval. Successful recurring billing is not business approval (business_approval is not derived from carrying).",
          "You want to confirm that amounts, currencies, taxes or fees are correct. This Tsugite does not check them (that is the role of external decisions and of people and organizations).",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, it does not send operations that move money (REFUSED).",
          "It checks the idempotency key. It does not send a billing (BILL_CYCLE) without an idempotency key, or a request that reuses a key with different content.",
          "A receipt that claims an effect for a duplicate is not accepted. The effect of a duplicate (DEDUP_HIT) is NONE.",
          "It checks the pinned lists. It does not send a payment method that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "It matches the receipt. A receipt whose state does not match op, a receipt that claims an effect for a decline, a receipt that increases the external effect, or a receipt for another request (a spoofed request_digest) is not accepted, and the client stops from then on (HALTED).",
          "A receipt that leaves it unknown whether money moved (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED). The case is passed to a person for review.",
          "A receipt that claims business approval is not accepted.",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "Successful recurring billing is not business approval. Every result carries approval: false. Business approval is the role of the Kernel (approval-facts) and of people and organizations.",
          "It does not check whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass a SubscriptionBillingRequest to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries billing_state and business_approval in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for recurring billing operations; NONE for duplicates, declines, failures and similar.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-06",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Payment and remittance (payout) — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks a payout request (PayoutRequest) against a reference to evidence received from a person or organization, the pinned payout destinations and the setting that allows operations that move money (allow_money_movement), carries it to the payment platform, and matches the returned receipt (PayoutReceipt) against the contract. ERABAZU does not create payout authorization, and without evidence it does not send (fail-closed).",
        "problem": "When a payout to a business is carried to an external payment platform, this Tsugite prevents sending it without evidence from a person or organization, sending to a payout destination that is not pinned, and trusting a receipt that claims payout authorization. It is a check against mistaking the ability to pay for payout authorization.",
        "use_when": [
          "You want to carry a payout to a business only after receiving evidence issued by a person or organization with recordAuthorityEvidence(evidence_ref, origin) (origin is only HUMAN or ORGANIZATION; this operation also takes the evidence for accepting a chargeback).",
          "You want to limit payout destinations to a pinned list (payout_destinations).",
          "You want payouts to be sent only when it has been explicitly allowed by the setting allow_money_movement: true (by default nothing is sent).",
          "You do not want the same payout to run twice. An idempotency key is required, the same request_id is not sent twice, and a resend after a failure is resolved with a lookup (lookup)."
        ],
        "do_not_use_when": [
          "You want this Tsugite to issue payout authorization. ERABAZU does not create payout authorization. Executing a payout is the role of a person (the same approval boundary as the Kernels).",
          "You want to confirm that the evidence is genuine, or that amounts, currencies, taxes or fees are correct. This Tsugite checks neither (that is the role of external decisions and of people and organizations).",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the evidence. It does not send a payout without an evidence reference, a payout that uses evidence not received through recordAuthorityEvidence, or evidence whose origin is ERABAZU.",
          "A receipt that claims payout authorization is not accepted.",
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, it does not send operations that move money (REFUSED).",
          "It checks the pinned lists. It does not send a payout destination that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "It checks the idempotency key. It does not send a payout without an idempotency key, or a request that reuses a key with different content.",
          "It matches the receipt. A receipt whose state does not match op, a receipt that claims an effect for a decline, a receipt that increases the external effect, or a receipt for another request (a spoofed request_digest) is not accepted, and the client stops from then on (HALTED).",
          "A receipt that leaves it unknown whether money moved (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED). The case is passed to a person for review.",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "Being able to pay is not payout authorization. payout_authorization in the result is not derived from carrying either, and every result carries approval: false.",
          "It does not check the content of the payout authorization (whether the evidence is genuine) or whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: receive evidence with recordAuthorityEvidence(evidence_ref, origin) on createPaymentRailsClient({ cfg, port }), then pass a PayoutRequest to submit(request). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries payout_state and payout_authorization in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-07",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "Tax calculation — Tax calculation part (Kernel)",
        "summary": "The Tax calculation part (Kernel) is a standalone part that, for Japan's consumption tax (standard rate 10%, reduced rate 8%, former rate 8%), returns as facts, from the lines of one qualified invoice (or one simplified qualified invoice), the total consideration and the consumption tax for each tax rate, and the missing required entries. It holds no tax rates in code: it selects them from the tax rate table it is given (with periods and the national and local breakdown), and it selects the rounding method from the business's settings by the invoice date. What the table or the settings do not determine is not guessed but made need_input, and malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "Behind \"tax included\", it prevents rounding the tax line by line, floating-point errors entering the tax, calculating with tax rates held in code, and filling input that the table or the settings do not determine with guesses. The tax is rounded only once per tax rate for each invoice.",
        "use_when": [
          "You want to produce as facts, from the lines (1 to 1000) of one qualified invoice (QUALIFIED) or simplified qualified invoice (SIMPLIFIED), the total consideration and the consumption tax for each tax rate.",
          "You want the tax rounded only once per invoice for each tax rate (each set of the rate breakdown rate, national_rate and local_rate), by the method in the business's settings (FLOOR rounding down, CEIL rounding up, HALF_UP rounding half up). It does not round per line or per table row.",
          "You want the tax computed as an exact fraction without floating point, according to whether the invoice is tax-exclusive (TAX_EXCLUDED) or tax-inclusive (TAX_INCLUDED), one of the two per invoice. Monetary values and rates are passed as decimal strings.",
          "You want tax rates selected from a tax rate table the user passes (each row with its period, national and local breakdown, and source). bundledTaxTable() returns a copy of the bundled table, and another table can also be passed as tax_table.",
          "You want the missing required entries (name or business name, registration number, transaction date, description of the transaction, the indication that an item is subject to the reduced tax rate, and so on) listed in the same call as the calculation. Missing entries do not stop the calculation.",
          "You need the same invoice_digest (the sha256 of the RFC 8785 canonical JSON) and byte-for-byte identical output from the same invoice regardless of key order. It does not use the clock, random numbers or the environment's locale."
        ],
        "do_not_use_when": [
          "You need period aggregation (the accumulation-method calculation of output tax and input tax, or the choice between it and the division method), the deemed purchase rates of simplified taxation, the 20% special rule, or import consumption tax. This part only does calculations closed within one invoice.",
          "You need the determination of non-taxable, out-of-scope or exempt status, withholding tax, or another country's taxes. Of the catalog one-liner \"consumption tax, withholding and so on\", withholding tax is not included.",
          "You want it to determine whether an item is subject to the reduced tax rate or whether a transitional measure applies. The caller decides these and passes them through rate_class and by naming a rate_id.",
          "You want to check whether a registration number is in the register on the National Tax Agency's publication site. It checks only the form of T followed by 13 digits and does not query the register.",
          "You handle invoices containing negative values (discounts, returns, refunds of consideration and so on). v1 does not accept negative values and stops with NEGATIVE_AMOUNT.",
          "You handle values in another country's currency. It stops with UNSUPPORTED_CURRENCY.",
          "You need the tax divided between national and local. For the national and local breakdown it outputs only the rates.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding exactly the three keys { invoice, tax_table, taxpayer_profile }, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays or a __proto__ key, and checks the values after copying them into plain data.",
          "When the invoice is malformed (an unknown key, a missing key, a value outside the vocabulary, a lone surrogate, 0 lines or more than 1000 lines, a reduced_marker other than true), it stops with INVALID_INVOICE, when a date is not a real calendar date, with INVALID_DATE, and when two lines have the same line_id, with DUPLICATE_LINE_ID.",
          "When a monetary value is not in the fixed form { value, scale, currency } (scale is not an integer from 0 to 6, or the number of decimal places differs from scale), it stops with MALFORMED_AMOUNT, when it is negative, with NEGATIVE_AMOUNT, and when the currency differs, with UNSUPPORTED_CURRENCY.",
          "When the tax rate table is malformed (including a source url that is not https), it stops with INVALID_TAX_TABLE, when a row has no source, with MISSING_SOURCE, when a rate is not in the fixed form or is 0, with MALFORMED_RATE, and when national_rate + local_rate does not equal rate, with RATE_SPLIT_MISMATCH.",
          "When a period ends before it begins, it stops with INVALID_PERIOD, when two rows have the same rate_id, with DUPLICATE_RATE_ID, when the periods of rows of the same class overlap, with OVERLAPPING_RATE_ROWS, and when the business's settings are malformed (a rounding method other than FLOOR, CEIL or HALF_UP and so on), with INVALID_PROFILE.",
          "When there is no transaction date, when the table has no row for the transaction date, when a named rate_id is not in the table, is of a different class or does not match the date, or when 0 or 2 or more rounding settings cover the invoice date, it does not guess: it returns need_input, with groups empty and invoice_totals null (it does not output a partial calculation).",
          "Of the required entries (Consumption Tax Act Article 57-4 paragraphs 1 and 2, Invoice Q&A questions 54 and 58), it checks the name or business name, the registration number (form only), the transaction date, the description of the transaction with the indication that an item is subject to the reduced tax rate, and the name of the receiving business (qualified invoices only) from the input fields, and whether the totals per tax rate, the applied rate and the tax are shown from the declaration in presentation, and lists what is absent in missing of required_items.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only tax facts. The fields inference and advice are always null, and no string in the output contains wording on how to fix something or a judgement. The field approval is false and authority is NOT_DERIVED (derives_authority: false).",
          "The results are a presentation of facts: they are not a tax judgement, they do not show that a tax return is correct, and they are not acting on anyone's behalf in tax judgements or tax returns. Tax consultation is a service reserved exclusively for licensed tax accountants (zeirishi). Whether a transitional measure applies, the classification of items and whether a registration number is in the register are checked outside this part.",
          "It does not handle period aggregation (whether the choice between the accumulation method and the division method is appropriate), simplified taxation, the 20% special rule, import consumption tax, the determination of non-taxable, out-of-scope or exempt status, withholding tax, or another country's taxes.",
          "It does not check that a reduced_marker is attached to a standard-rate line (and does not report it as a missing entry either). The fields sales_tax_method and taxation_method are only checked for form and copied; they are not used in the calculation.",
          "When tax-inclusive values containing fractions below the smallest currency unit are passed and rounding up or rounding half up makes the tax exceed the total, the tax-exclusive total becomes negative. It outputs this as a fact as it is and does not correct it.",
          "The bundled table has only 3 rows, based on National Tax Agency materials as of 2026-10-01. Tax rates before 2014-04-01 and amendment proposals that have not been enacted are not included. A transaction date in a period with no row in the table becomes need_input.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call computeInvoiceTax({ invoice, tax_table, taxpayer_profile }) in src/tax-facts.js (it does not throw exceptions). Input: the invoice (lines, presentation and so on), the tax rate table (each row's rate breakdown, period and source) and the business's settings (rounding). Returns: { ok: true, errors: [], facts } (groups, invoice_totals, required_items and need_input, with inference: null and advice: null) or { ok: false, errors: [{ code, where }] } (where is a JSON Pointer). Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-08",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Exchange rate retrieval — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks an exchange rate request (FxRateRequest) against the pinned currency pairs (fx_pairs) and rate sources (rate_sources), carries it, and matches the returned receipt (FxRateReceipt) against the contract. An observed rate is not authoritative, and a receipt that claims authority is not accepted (fail-closed).",
        "problem": "When exchange rates are fetched from outside, this Tsugite prevents using rates for currency pairs or from sources that are not pinned, and treating an observed rate as authoritative.",
        "use_when": [
          "You want to observe and carry exchange rates only for the pinned currency pairs and sources, and receive them as a result (rate_state, rate_ref).",
          "You want to use received rates on the premise that they are observed values, not authoritative ones (rate_authority is not derived from carrying)."
        ],
        "do_not_use_when": [
          "You want to convert between currencies (currency conversion). This Tsugite is for exchange rate retrieval (carrying observed rates) and does not include any conversion function.",
          "You want to treat an observed rate as an authoritative rate. An observed rate is not authoritative.",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the pinned lists. It does not send a currency pair that is not pinned, a rate source that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "A rate receipt that claims authority is not accepted.",
          "A receipt that increases the external effect is not accepted. The effect of an exchange rate request is NONE.",
          "A receipt whose state does not match op, or a receipt for another request (a spoofed request_digest), is not accepted, and the client stops from then on (HALTED).",
          "A receipt whose effect is unknown (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED).",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "It is a Tsugite that carries exchange rate requests; it does not include any conversion function (converting between currencies).",
          "An observed rate is not authoritative. Every result carries approval: false.",
          "It does not check whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass a FxRateRequest to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries rate_state, rate_ref and rate_authority in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-09",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Statement intake for reconciliation (processor) — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks a statement intake request (StatementIntakeRequest) against the pinned statement issuers (processors), carries it, and matches the returned receipt (StatementIntakeReceipt) against the contract. Carrying statements is not reconciliation, and a receipt that claims reconciliation is not accepted (fail-closed).",
        "problem": "When payment statements are taken in from outside, this Tsugite prevents taking in statements from an issuer that is not pinned, and mistaking the carrying of statements for completed reconciliation of statements against deposits.",
        "use_when": [
          "You want to take in statements only from the pinned statement issuers and receive them as a result (intake_state), leaving reconciliation to the Kernel (reconcile-facts) or to people and organizations.",
          "You want to handle intake and reconciliation separately. A receipt that claims reconciliation is not accepted, and reconciliation is not derived from carrying."
        ],
        "do_not_use_when": [
          "You want to reconcile statements against deposits. This Tsugite does not reconcile. Reconciling statements is the role of external decisions and of people and organizations.",
          "You want to confirm that the amounts, currencies, taxes or fees in statements are correct. This Tsugite does not check them.",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the pinned lists. It does not send a statement issuer that is not pinned, or a reference that is empty or contains a path or .. (path traversal). An empty list allows nothing.",
          "A receipt that claims reconciliation is not accepted.",
          "A receipt that increases the external effect is not accepted. The effect of statement intake is NONE or EXTERNAL_REVERSIBLE (NONE for FAILED).",
          "A receipt whose state does not match op, or a receipt for another request (a spoofed request_digest), is not accepted, and the client stops from then on (HALTED).",
          "A receipt whose effect is unknown (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED).",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "It does not reconcile statements. Carrying statements is not reconciliation, and reconciliation in the result is not derived from carrying either.",
          "Successful intake is not approval. Every result carries approval: false.",
          "It does not check whether amounts, currencies, taxes or fees are correct (that is the role of external decisions and of people and organizations).",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: pass a StatementIntakeRequest to submit(request) on createPaymentRailsClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries intake_state and reconciliation in result. Every result has approval: false. User provides: a port to the payment platform (submit, and optionally lookup). Side effect: NONE or EXTERNAL_REVERSIBLE (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-10",
        "category": "G/決済・資金",
        "kind": "Tsugite",
        "title": "Chargeback disputes — Payment Rails Tsugite",
        "summary": "The Payment Rails Tsugite (継手, tsugite) checks the filing (OPEN), evidence submission (SUBMIT_EVIDENCE), observation (OBSERVE) and acceptance (ACCEPT) operations of a chargeback request (DisputeRequest) before carrying them, and matches that the returned receipt (DisputeReceipt) is in the state that fits op. It sends an acceptance only after receiving evidence from a person or organization, and only when the setting allows it as an operation that moves money. Carrying is not resolution authority (fail-closed).",
        "problem": "When chargebacks are carried to an external payment platform, this Tsugite prevents sending an evidence submission without an evidence reference, sending an acceptance (ACCEPT) without evidence from a person or organization, and trusting a receipt that claims resolution authority. Acceptance is a business decision that cannot be undone, so it is carried only when there is evidence.",
        "use_when": [
          "You want to carry chargeback filing (OPEN to OPENED), evidence submission (SUBMIT_EVIDENCE to EVIDENCE_SUBMITTED), observation (OBSERVE to OBSERVED) and acceptance (ACCEPT to ACCEPTED), and match that the receipt is in a state that fits op.",
          "You want to leave the resolution of chargebacks to the decisions of people and organizations, and not derive resolution authority (resolution_authority) from carrying.",
          "You want to carry a chargeback acceptance (ACCEPT) only after receiving evidence issued by a person or organization with recordAuthorityEvidence, and only when it has been allowed by the setting allow_money_movement: true."
        ],
        "do_not_use_when": [
          "You want to accept a chargeback (ACCEPT) without evidence from a person or organization, or while the setting allow_money_movement is not true. This Tsugite does not send it (REFUSED).",
          "You want to resolve a chargeback or issue a refund. This Tsugite carries chargeback filing, evidence submission, observation and acceptance; it has no resolution authority and does not include any refund function. Carrying an acceptance is not resolution.",
          "You want to confirm that the evidence for an acceptance is a genuine decision. This Tsugite only looks at whether it is a reference received from a person or organization.",
          "You want to connect to an actual payment platform right away. It has never been connected to an actual payment platform, PSP or bank, and it does not include a port implementation.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the evidence for an acceptance. It does not send an acceptance (ACCEPT) without an evidence reference, an acceptance that uses evidence not received with recordAuthorityEvidence, or an acceptance whose evidence has origin ERABAZU. It does not send a request that attaches the evidence fields to an op other than acceptance either.",
          "It checks the permission to move money. When allow_money_movement is not true, or is not a boolean, an acceptance (ACCEPT) is not sent (REFUSED).",
          "It does not send an evidence submission without an evidence reference. It does not send one whose reference is empty or contains a path or .. either.",
          "A receipt that claims resolution authority is not accepted.",
          "A receipt that increases the external effect is not accepted. The effect is NONE for observation, EXTERNAL_REVERSIBLE for filing and evidence submission, and EXTERNAL_IRREVERSIBLE for acceptance (NONE for FAILED).",
          "A receipt whose state does not match op, or a receipt for another request (a spoofed request_digest), is not accepted, and the client stops from then on (HALTED).",
          "A receipt whose effect is unknown (effect UNKNOWN) is not accepted, and the client stops from then on (HALTED).",
          "It does not send the same request_id twice. A payment platform failure is treated as UNKNOWN, and resending is resolved with a lookup (lookup) so that nothing runs twice."
        ],
        "what_it_does_not_do": [
          "It does not resolve chargebacks. Carrying an acceptance (ACCEPT) is not resolution authority, and every result carries approval: false. Resolution is the role of external decisions and of people and organizations.",
          "It does not confirm the content of the evidence for an acceptance (whether it is a genuine decision). It only looks at whether it is a reference received from a person or organization. ERABAZU does not create a decision to concede the dispute.",
          "It has never been connected to an actual payment platform, PSP or bank. The number of actual charges or payouts sent is 0. The port implementation, the pinned lists and the permission to move money will be settled at a separate gate when connecting.",
          "It does not hold the secret used to authenticate to the payment platform. The secret is held on the port side (HELD_BY_PORT).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a specific payment service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/payment-rails/v1. Input: after receiving the evidence with recordAuthorityEvidence on createPaymentRailsClient({ cfg, port }), pass a DisputeRequest to submit(request). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries dispute_state and resolution_authority in result. Every result has approval: false. User provides: a port. Side effect: NONE for observation, EXTERNAL_REVERSIBLE for filing and evidence submission, and EXTERNAL_IRREVERSIBLE for acceptance.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-11",
        "category": "G/決済・資金",
        "kind": "Primitive",
        "title": "Decimal representation of amounts — Decimal representation of amounts (string + scale) part (Primitive)",
        "summary": "The Decimal representation of amounts (string + scale) part (Primitive) is a standalone part that holds an amount as { value, scale, currency } (a decimal integer string, the position of the decimal point, and a currency code), and reads amounts from decimal strings, normalizes them by removing trailing zeros, adds, compares and tests equality of amounts in the same currency, and writes them out as decimal strings. It computes only with BigInt, does not use floating point, and does not round.",
        "problem": "It prevents accidents in which an amount drifts by a fraction because of decimal error. Because it does not use floating point, no such error enters, and a result that does not fit in 38 digits is stopped rather than silently rounded. Values it cannot accept are stopped with a fixed code and location.",
        "use_when": [
          "You want to read a decimal string (-?[0-9]+(.[0-9]+)?, ASCII digits only), together with a currency code, as an amount (parseDecimal). scale stays the number of decimal digits as written; it is not normalized.",
          "You want to bring an amount to the smallest scale by removing trailing zeros (normalize; zero is { value: \"0\", scale: 0 }).",
          "You want to add two amounts in the same currency without rounding (add; the result is a normalized amount).",
          "You want to know the numeric order (-1, 0 or 1 from compare) or equality (equal) regardless of scale. equal is false when the currencies differ, which is not an error.",
          "You want to write an amount out as a decimal string with exactly scale decimal digits (toText). parseDecimal(toText(a)) returns to the same value and scale.",
          "You want to call it locally as deterministic processing that has no clock, no random numbers, no environment locale and no outside connection."
        ],
        "do_not_use_when": [
          "You need rounding, multiplication, division or currency conversion. This part does not handle them.",
          "You need a display string with symbols or digit grouping separators. Formatting for display is the role of the L-03 part.",
          "You want to confirm that a currency code is in the ISO 4217 table, or that an amount matches the number of decimal digits for its currency. This part checks only the form (3 uppercase letters).",
          "You handle amounts beyond 38 digits or scale 18. They cannot be represented, so it stops (TOO_MANY_DIGITS, INVALID_SCALE).",
          "You want leading zeros in the integer part to be refused. parseDecimal accepts leading zeros and drops them. If you need a narrower form, the caller checks it.",
          "You need an upper limit on the length of text. It has none; the size limit is decided by the I-06 part or by the caller.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_ARGUMENT when the argument is not a plain object, has keys other than the fixed ones, has getters, non-enumerable properties or symbol keys, or when inspecting its values itself raises an exception (a Proxy trap and so on). It does not call getters.",
          "It stops with NOT_A_STRING when text or value is not a string (a value passed as a number, NaN, Infinity, a bigint, a missing key).",
          "It stops with INVALID_DECIMAL_TEXT when text is not of the form -?[0-9]+(.[0-9]+)? (exponents, +, white space, digit grouping separators, \"1.\", \".5\", full-width digits, line breaks).",
          "It stops with INVALID_VALUE when value is not of the form -?[0-9]+ or has extra leading zeros, and with NEGATIVE_ZERO for a negative zero such as \"-0\".",
          "It stops with TOO_MANY_DIGITS when there are more than 38 digits (including the result of an addition), and with INVALID_SCALE when scale is not an integer from 0 to 18 (-0, a fraction, a string) or when there are more than 18 decimal digits.",
          "It stops with INVALID_CURRENCY when currency is not a string of 3 uppercase letters, and with CURRENCY_MISMATCH when the two amounts given to add or compare are in different currencies.",
          "When it stops, it returns only the first problem, in a fixed order, as a code (code) and a location (where; a JSON Pointer, which starts with /a or /b for operations on two amounts)."
        ],
        "what_it_does_not_do": [
          "It does not do rounding, multiplication, division, currency conversion or formatting for display.",
          "Being able to compute does not mean the amount is correct for the business or that it may be accepted.",
          "It does not check against the number of decimal digits for each currency. It accepts a code that is not in the ISO 4217 table if the form matches.",
          "It does not accept full-width digits or digits from other writing systems. Digits are only ASCII 0 to 9.",
          "Computation results are not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular decimal library."
        ],
        "integration": "Call parseDecimal({ text, currency }), normalize(amount), add(a, b), compare(a, b), equal(a, b) and toText(amount) in src/decimal-amount.js (none of them throw). An amount is { value, scale, currency } (value is an integer string of up to 38 digits excluding the sign, scale is an integer from 0 to 18, and currency is 3 uppercase letters). It returns { ok: true, errors: [], amount | result | equal | text } or { ok: false, errors: [{ code, where }] }. There are no side effects, no state, no outside connection and no credentials, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-12",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "Transaction event ID — Transaction event ID (txn_id) part (Kernel)",
        "summary": "The Transaction event ID (txn_id) part (Kernel) is a standalone part that produces an event ID (txn_id) by fixed rules from the facts of a transaction or a piece of evidence (kind, source, occurred_on, amount, counterparty). It turns the form to be hashed into RFC 8785 canonical JSON, takes its SHA-256, and writes the first 16 bytes as 32 lower-case hex characters. When a required fact is absent it does not guess but returns need_input, and malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "A matching key that prevents double counting needs the same value from the same facts every time. It prevents the ID of the same facts from changing because of trailing zeros in an amount and how scale is written, the order of the input keys, floating-point errors, or references to where a record came from such as raw_refs.",
        "use_when": [
          "You want to create, from the facts of a transaction or a piece of evidence, an event ID (txn_id) that takes the same value from the same facts, as a matching key that prevents double counting.",
          "You want amounts of the same value to get the same ID however scale is written. The amount has its trailing zeros removed with BigInt so that scale is minimal, and floating point is not used. A different currency, sign or value gives a different ID.",
          "You want to identify the counterparty by its ID in the source (id_in_source) or by name. When id_in_source is present and not empty, only it is used; otherwise the name after NFKC with leading and trailing whitespace removed is used.",
          "When required facts are absent, you want to receive missing, the list of the locations (JSON Pointer) of the absent facts, instead of having them filled by guesses. The state is need_input, and this is not an error.",
          "Along with the ID, you want to receive the form that was hashed (hashed) and its canonical JSON string (canonical), to check what the ID was made from.",
          "You need deterministic output that does not use the clock, random numbers or the environment's locale and does not depend on the order of the input keys. There is no external boundary and no state."
        ],
        "do_not_use_when": [
          "You want it to decide whether two records are the same transaction. Separate transactions with the same field values (two purchases on the same day for the same amount from the same counterparty, and so on) get the same txn_id. Telling them apart is the role of source_key, which the G-13 and G-14 parts handle.",
          "You want variations in notation brought together. Differences in upper and lower case, (株) versus 株式会社, zero-width characters and the number of spaces give different IDs. Proposing alias candidates is not part of this part.",
          "You want it to decide kind (evidence or transaction), source or id_in_source. The caller decides all of them and passes them. kind is used as declared and is not determined by this part.",
          "You need record-form checks, handling of supersedes, or grouping of duplicates. The record form and supersedes are within the scope of the G-13 part, and the identity criteria within the scope of the G-14 part.",
          "You want to rely on IDs never colliding. txn_id is a hash truncated to 128 bits, and the absence of collisions is not promised.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED). NFKC comes from String.prototype.normalize in Node.js (ICU), so results can change when the Unicode version changes."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the six keys (kind, source, occurred_on, amount, counterparty, raw_refs), when it has getters, non-enumerable properties or symbol keys (except in raw_refs), or when inspecting the values itself throws an exception (a Proxy trap and so on), it stops with INVALID_ARGUMENT.",
          "When kind is not evidence or transaction, it stops with INVALID_KIND, when source is not a string matching ^[a-z0-9][a-z0-9_-]{0,62}$, with INVALID_SOURCE, and when occurred_on is not a real calendar date in YYYY-MM-DD form (years 0001 to 9999), with INVALID_DATE.",
          "When amount is not a plain object holding only { value, scale, currency }, when value is not of the form -?[0-9]+ (a leading zero, \"-0\", or more than 38 digits excluding the sign), when scale is not an integer from 0 to 18 (including -0), or when currency is not 3 upper-case letters, it stops with INVALID_AMOUNT.",
          "When counterparty is not a plain object holding only keys from { id_in_source, name }, when either is not a string or null, or when it contains a lone surrogate, it stops with INVALID_COUNTERPARTY. The form of the name is checked even when id_in_source is present.",
          "If even one field is malformed, the result is an error ({ ok: false }) even when other fields are absent, and only the first one found is returned. The order of checks is the form of the argument, then kind, source, occurred_on, amount and counterparty.",
          "It does not read raw_refs (it neither checks their form nor calls getters) and does not put them into the ID. The values checked and the values hashed are the same.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src, src/canonical.js or crypto (its SHA-256 is also pure JS) confirmed that mismatches and fail-open cases were 0. Only for NFKC does the oracle use the same function.",
          "It is tested with 15 known answers (test/known-answers.json). The canonical strings were written by hand from the rules, and each txn_id was computed with a pure-JS SHA-256 that does not use src. The same answers are shared with the G-13 part."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null, and no string in the output contains wording of a judgement. The result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The event ID is a value made from fixed fields by fixed rules; it is not a bookkeeping or reconciliation judgement. It does not decide whether two records are the same transaction.",
          "It does not normalize id_in_source (it uses it as is, including leading and trailing whitespace). For names it only applies NFKC and removes leading and trailing whitespace, and brings nothing further together.",
          "It checks only the form of the currency code (3 upper-case letters) and does not compare it with the ISO 4217 table.",
          "It has no length limit for id_in_source and name. NFKC, canonicalization and hashing take time in proportion to the length. The size limit is set by the I-06 part or by the user.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call computeTxnId(input) in src/txn-id.js (it does not throw exceptions). Input: { kind, source, occurred_on, amount: { value, scale, currency }, counterparty: { id_in_source?, name? }, raw_refs? }. Returns: { ok: true, errors: [], facts } (state, txn_id, hashed, canonical and missing, with inference: null and advice: null) or { ok: false, errors: [{ code, where }] } (where is a JSON Pointer). Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-13",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "Transaction and match record checks — Common-form transaction and match record check part (Kernel)",
        "summary": "The Common-form transaction and match record check part (Kernel) is a standalone part that checks every field of a common-form transaction (erabazu.common.txn/1) and confirms that its txn_id equals the value recomputed by the event ID rules of G-12. A match (erabazu.common.match/1) is checked as a record separate from the transaction. For a set of transactions it outputs the facts of the supersedes chains and the chains in order from oldest, but it does not decide which version is correct. Malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "The form of the records that books are built from tends to be used without anyone looking at it. It prevents trusting the txn_id written in a record at its word, writing match information into the transaction record, and failing to notice breaks, cycles and forks in supersedes chains.",
        "use_when": [
          "You want to check one common-form transaction as a closed form (schema, txn_id, kind, occurred_on, amount, counterparty, lines, source, raw_refs, probe_version and extracted_at, plus the optional supersedes and source_key) (checkTxn).",
          "You want to confirm that the txn_id of a record equals the value computed by the rules of G-12 from kind, source, occurred_on, amount and counterparty. If they differ, it returns TXN_ID_MISMATCH and the recomputed value recomputed_txn_id.",
          "You want to check a match record (evidence_txn, transaction_txn, basis, matcher_version, confidence) as a record separate from the transaction (checkMatch). A match record that has transaction fields is not accepted.",
          "For a set of transactions (up to 10,000), you want the supersedes chains arranged in order from oldest, and to receive as problems the cases of a missing target, the same id, a different source, a cycle and a fork (checkSupersedesChain).",
          "You need deterministic output that does not use the clock, random numbers or the environment's locale. The result of checkSupersedesChain also does not depend on the order of the input. There is no external boundary and no state."
        ],
        "do_not_use_when": [
          "You want it to decide which version is correct or which one to use. It only lists the facts of the supersedes chains.",
          "You need valuation (currency conversion and so on) or grouping of duplicates. Grouping is within the scope of the G-14 part.",
          "You want to confirm that the evidence_txn and transaction_txn of a match are real transactions, or that the kind of each is right. It checks only the form of the ids and that the two differ.",
          "You want to confirm that the sum of the line amounts matches the transaction amount, or that the currency of the lines is the same as the currency of the transaction. It checks neither.",
          "You want to collect absent facts. When a record lacks fields, it stops with INVALID_RECORD rather than returning need_input. This part checks stored records, and its role differs from G-12, which outputs the list of absent facts.",
          "You want to handle, in the same set, a correction whose txn_id does not change even though the content was fixed. A set with two items with the same txn_id stops with DUPLICATE_TXN_ID.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED). NFKC depends on the runtime's ICU, so the result of name normalization can change when the Unicode version changes."
        ],
        "what_it_checks": [
          "When a record is not plain data (getters, class instances, sparse arrays, symbol keys, a __proto__ key, nesting deeper than 8 levels or arrays of more than 10,000 elements), or has an unknown key or a missing key, it stops with INVALID_RECORD, and when inspecting the values itself throws an exception, with INSPECTION_FAILED.",
          "When schema differs, it stops with INVALID_SCHEMA, when txn_id, supersedes and the like are not 32 lower-case hex characters, with INVALID_TXN_ID, when kind is outside the vocabulary, with INVALID_KIND, when a date is not a real calendar date, with INVALID_DATE, and when an amount is malformed, with INVALID_AMOUNT.",
          "When the counterparty is empty (id_in_source is empty and name is also empty after NFKC and trim) or malformed, it stops with INVALID_COUNTERPARTY, when lines or quantities are malformed, with INVALID_LINES, when source is malformed, with INVALID_SOURCE, and when raw_refs is not an array of 1 to 1024 Refs without duplicates, with INVALID_RAW_REFS.",
          "When probe_version is malformed, it stops with INVALID_PROBE_VERSION, when extracted_at is not an RFC 3339 date-time (including a leap second or a lower-case t or z), with INVALID_TIME, and when source_key is malformed, with INVALID_SOURCE_KEY. The checks run in a fixed order, and only the first one hit is returned.",
          "It does not trust the txn_id at the record's word: it recomputes it by the rules of G-12 and compares. If they differ, it stops with TXN_ID_MISMATCH.",
          "When a match record is not plain data, or has an unknown key (including transaction fields) or a missing key, or when basis, matcher_version or confidence (exact, probable, manual) is malformed, it stops with INVALID_MATCH, and when the two ids are the same, with SAME_TXN_MATCHED.",
          "When the chain argument is not a plain object holding only { txns }, it stops with INVALID_ARGUMENT, and when two transactions have the same txn_id, with DUPLICATE_TXN_ID. Problem edges (a missing target, the same id, a different source, a fork, a cycle) are not linked into a chain and are output in problems as facts with codes beginning with SUPERSEDES_.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0. For NFKC and SHA-256, the oracle also uses the same Node.js functions."
        ],
        "what_it_does_not_do": [
          "It returns only facts about the form of the records and their agreement with the rules. The fields inference and advice are always null, approval is false, and it creates no authority (derives_authority: false). Name and description strings from the input are copied as they are.",
          "The results are not a judgement that the content of a transaction is correct or that a given version is correct. Nor are they accounting judgements or bookkeeping done on anyone's behalf. Whether a match is right, whether a kind is right and the valuation of amounts (currency and so on) are outside this part.",
          "It takes kind (evidence or transaction) as declared and does not determine it.",
          "It does not check whether the retrieved items that raw_refs point to exist, or whether probe_version is a real version (form only). It also checks only the form of the 3 upper-case letters of a currency and does not compare them with the ISO 4217 list.",
          "It does not load the G-12 part itself; it holds a copy of the rules inside. That the copy is the same was confirmed by checking the known answers of both in the tests and by a one-time comparison.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call checkTxn(record), checkMatch(record) and checkSupersedesChain({ txns }) in src/txn-record.js (they do not throw exceptions and do not modify the input). Returns: { ok: true, errors: [], facts } or { ok: false, errors: [{ code, where }] } (where is a JSON Pointer; TXN_ID_MISMATCH also carries recomputed_txn_id). The facts of checkTxn are txn_id, hashed, canonical, supersedes and source_key; for chains they are chains and problems. Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "G-14",
        "category": "G/決済・資金",
        "kind": "Kernel",
        "title": "Transaction identity criteria — Transaction identity criteria part (Kernel)",
        "summary": "The Transaction identity criteria part (Kernel) is a standalone part that divides transaction records into groups with the same key, following the identity criterion declared by the caller (source_key, or composite_key chosen from the date, the amount and the counterparty). Records without the declared key are not guessed but listed in need_input. When fields differ within a group it only sets differing: true; it neither merges nor deletes. It does no fuzzy matching. The fields inference and advice are always null.",
        "problem": "As a foundation for judging whether records are the same transaction, it groups records that get the same key under the declared criterion. It prevents filling records that have no key with guesses, bringing similar names, nearby dates or nearby amounts together as the same thing, and discarding the differences between records when grouping them.",
        "use_when": [
          "Records have the platform's transaction id (source_key), and you want to group them with only that as the key (basis: source_key). identity_key is the string \"sk:\" joined with source_key.",
          "There is no id, and you want to choose 1 to 3 of the date (occurred_on), the amount (amount) and the counterparty (counterparty) as the key (basis: composite_key). The key is the SHA-256 (64 lower-case hex characters) of the RFC 8785 canonical JSON of the chosen fields only.",
          "You want differences in the scale of amounts or between full-width and half-width characters in counterparty names to give the same key, with the same normalization as the event ID of G-12. The amount has its trailing zeros removed so that scale is minimal; for the counterparty, id_in_source alone is used if it is not empty, and otherwise the name after NFKC with leading and trailing whitespace removed.",
          "You want records without the declared key returned as need_input, without a key being made by guessing. Each record goes exactly once into either groups or need_input.",
          "You want to know, as differing, that the presence or values of record fields differ within a group with the same key. Values are compared as written, so differences in representation such as scale or full-width versus half-width are also counted as differences.",
          "You need deterministic output that does not use the clock, random numbers or the environment's locale. groups are in identity_key order, and refs and need_input are in input order."
        ],
        "do_not_use_when": [
          "You want candidates with similar names, nearby dates or nearby amounts. It does no fuzzy matching. Counterparty names only go through NFKC and removal of leading and trailing whitespace, and nothing further is brought together.",
          "You want it to merge or delete records, or to decide which one to keep (supersedes). It only outputs the groups.",
          "You want to mix records from other sources and group them by source_key. Records have no source field, so even if source_key happens to be the same, they are not told apart. The caller passes the records per source.",
          "When the declaration is composite_key, you also want source_key used as part of the key. Following the declaration, source_key is not used for the key (it is counted for differing).",
          "You want the part to choose the criterion. Which criterion to use is declared by the caller.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED). NFKC depends on the runtime's ICU, so the result of name normalization can change when the Unicode version changes."
        ],
        "what_it_checks": [
          "When the argument is not a plain object with only the two data properties { declaration, records }, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays or a __proto__ key.",
          "When the basis of the declaration is not source_key or composite_key, when it has an unknown key (including source_key with fields attached), or when fields is not an array of 1 to 3 non-overlapping items from occurred_on, amount and counterparty, it stops with INVALID_DECLARATION.",
          "When records is not an array of plain data (up to 10,000 items, nesting up to 8 levels), or when a record has an unknown key or has no ref or a ref not in Ref form, it stops with INVALID_RECORDS, and when two records have the same ref, with DUPLICATE_REF.",
          "When a given field (source_key, occurred_on, amount, counterparty) is malformed, it stops with INVALID_FIELD, even for a field the declaration does not use. An empty source_key string is also stopped. A counterparty that is { } or has only empty strings is treated not as a form error but as an absent field (need_input).",
          "The checks run in a fixed order (the argument, then declaration, then the form of each record, then overlapping refs), and only the first one hit is returned.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0. For NFKC and SHA-256, the oracle also uses the same Node.js functions."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null, approval is false, and it creates no authority (derives_authority: false). The ref and source_key values from the input are copied as they are.",
          "The results are facts about whether records get the same key under the declared criterion; they are not a judgement that records are the same transaction or of which one to keep. Nor are they duplicate judgements or bookkeeping done on anyone's behalf.",
          "It does not include proposing fuzzy duplicate candidates (suggest-dup), bringing aliases together (suggest-alias), merging, deleting, or deciding supersedes.",
          "It checks only the form of the 3 upper-case letters of a currency and does not compare them with the ISO 4217 list.",
          "It does not load the G-12 part itself; it holds a copy of the normalization rules inside. The copy was confirmed with 4 known answers computed with sha256sum outside Node.js.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call groupByIdentity({ declaration, records }) in src/identity-rule.js (it does not throw exceptions). declaration is { basis: \"source_key\" | \"composite_key\", fields? }, and records is an array of { ref, source_key?, occurred_on?, amount?, counterparty? }. Returns: { ok: true, errors: [], facts } (groups, need_input and so on, with inference: null and advice: null) or { ok: false, errors: [{ code, where }] }. There is no side effect, no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-01",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Append-only audit log — Audit Ledger Tsugite",
        "summary": "The Audit Ledger Tsugite (継手, tsugite) sends only checked audit event envelopes (AuditEventEnvelope) to an append-only backend, checks the receipt the backend returns (AuditAppendReceipt) against the contract's formulas, and only then advances the head of the ledger. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When audit records are sent to an external append-only store, it prevents sending malformed envelopes or envelopes that contain a body, and prevents treating a write as recorded without checking that the store's receipt is correct. It is a check so that an append that cannot be matched is never trusted.",
        "use_when": [
          "You want to record an audit log of who did what to an external append-only backend, in a form that cannot be erased later.",
          "You do not want to trust the receipt returned by the backend as is, and want to match it against the contract's formulas (chain hash, sequence and digest) before treating the event as recorded.",
          "You do not want the same event to be appended twice. When resending before a receipt is settled, the append is resolved by a lookup.",
          "You want appending to stop until a person checks it when a receipt is inconsistent."
        ],
        "do_not_use_when": [
          "You want to store the body of audit events itself. The envelope carries only the contract's fields, and a body mixed into it is rejected.",
          "You need a transparency log (Merkle inclusion proofs). That is outside the scope of this Tsugite.",
          "You want to treat a successful append as approval or a decision. A successful append is not approval.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a store you can use right away. No actual backend is included, and which append-only service to connect to has not been decided."
        ],
        "what_it_checks": [
          "It checks the envelope's type. Envelopes with additional fields, values that differ from the fixed values (const), or violations of the genesis rules are not sent.",
          "It checks the pinned ledger. Appends to a ledger that is not pinned are not sent.",
          "It checks the chain (previous_hash) and the sequence. If the previous hash has been tampered with, or the sequence rolls back or skips, it does not send and does not accept.",
          "It checks the digest. Hashes are computed with the contract's formula (a restricted form of RFC 8785 canonicalization and sha256; for genesis, previous_hash_value is the empty string), and anything that differs from the formula is not accepted.",
          "It checks the receipt (AuditAppendReceipt). If event_digest is spoofed, the chain hash is wrong, or the ledger, sequence, event_id or target does not match, the receipt is not accepted.",
          "If a receipt is inconsistent, it stops (HALTED) and does not append until a person checks it.",
          "A backend failure (a 500 response or a partial write) is treated as UNKNOWN, and the head of the ledger is not advanced.",
          "It rejects envelopes that contain a body, oversized events, envelopes with a spoofed kind (content-type spoofing), and duplicate appends of the same event."
        ],
        "what_it_does_not_do": [
          "Deciding whether records are unalterable is the role of the Kernels (provenance-facts and integrity-facts). This Tsugite only checks before sending and matches the receipt.",
          "A successful append is not approval (approval: false).",
          "It does not include an actual backend. Which append-only service to connect to, the port implementation and what to pin (endpoint and receipt format) are undecided and will be settled at a separate gate when connecting.",
          "It does not handle transparency logs (Merkle inclusion proofs).",
          "It does not resolve the secret (SecretRef) used for backend authentication. That is the responsibility of the port.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use an audit backend."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-ledger/v1. Input: pass an AuditEventEnvelope to append(envelope) on createLedgerClient({ cfg, port }). Returns: one of APPENDED_VERIFIED, ALREADY_APPENDED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the append-only backend (append, and optionally lookup). The pinned endpoint and receipt format, and resolving the SecretRef for authentication, are also on the port side. Side effect: EXTERNAL_IRREVERSIBLE (an external write that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-02",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Access log — Audit Ledger Tsugite",
        "summary": "The Audit Ledger Tsugite (継手, tsugite) is used to append records of access, that is, of who viewed what, to an append-only backend as audit events (AuditEventEnvelope). It sends only checked envelopes, and advances the head of the ledger only when the returned receipt (AuditAppendReceipt) is matched. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When access records are stacked on an external append-only store, it prevents sending malformed envelopes or envelopes that contain a body, duplicate appends of the same record, a sequence that rolls back or skips, and treating a write as recorded without checking that the store's receipt is correct.",
        "use_when": [
          "You want to append access records (who viewed what), one at a time, to an external append-only backend as audit events.",
          "You can provide the side that captures an access and turns it into an AuditEventEnvelope (the user's side) yourself, and want to leave only the append and matching of that envelope to this Tsugite.",
          "You want to match the receipt returned by the backend against the contract's formulas (chain hash, sequence and digest) before treating the record as recorded.",
          "You do not want the same access record to be appended twice. When resending before a receipt is settled, the append is resolved by a lookup.",
          "You want appending to stop until a person checks it when a receipt is inconsistent."
        ],
        "do_not_use_when": [
          "You want to detect or collect the accesses themselves. Capturing an access and turning it into an event is the role of the user's side; this Tsugite only carries the append and matches the receipt.",
          "You want to stack the content that was viewed (the body) in the ledger. The envelope carries only the contract's fields, and a body mixed into it is rejected.",
          "You want to rewrite or delete records. This Tsugite handles appends only.",
          "You need a transparency log (Merkle inclusion proofs). That is outside the scope of this Tsugite.",
          "You want a store you can use right away. No actual backend is included, and which append-only service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the envelope's type. Envelopes with additional fields, values that differ from the fixed values (const), violations of the genesis rules, or a spoofed kind (content-type spoofing) are not sent.",
          "It checks the pinned ledger. Appends to a ledger that is not pinned are not sent.",
          "It checks the chain (previous_hash) and the sequence. If the previous hash has been tampered with, or the sequence rolls back or skips, it does not send and does not accept.",
          "If the same event_id with the same content is already appended, it does not send to the backend and returns ALREADY_APPENDED; if the same event_id comes with different content, it returns REFUSED.",
          "It checks the receipt (AuditAppendReceipt). If event_digest is spoofed, the chain hash is wrong, or the ledger, sequence, event_id or target does not match, the receipt is not accepted.",
          "If a receipt is inconsistent, it stops (HALTED) and does not append until a person checks it.",
          "A backend failure (a 500 response or a partial write) is treated as UNKNOWN, and the head of the ledger is not advanced.",
          "It rejects envelopes that contain a body and events over the envelope size limit (max_envelope_bytes)."
        ],
        "what_it_does_not_do": [
          "It neither captures accesses nor creates access events. That is the role of the user's side; this Tsugite only carries the append and matches the receipt.",
          "A successful append is not approval (approval: false).",
          "It does not include an actual backend. Which append-only service to connect to, the port implementation and what to pin (endpoint and receipt format) are undecided and will be settled at a separate gate when connecting.",
          "It does not handle transparency logs (Merkle inclusion proofs). It also does not resolve the secret (SecretRef) used for backend authentication; that is the responsibility of the port.",
          "It is not a part dedicated to H-02. The same single part serves H-01 (append-only audit log), C-04 (append-only ledger) and H-08 (tamper-detection chain), and the tests and inspection are shared.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-ledger/v1. Input: pass an AuditEventEnvelope that represents an access record to append(envelope) on createLedgerClient({ cfg, port }). Returns: one of APPENDED_VERIFIED, ALREADY_APPENDED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: the processing that turns an access into an envelope, and a port to the append-only backend (append, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE (an external write that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-03",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Consent record store — Audit & Privacy Tsugite",
        "summary": "The Audit & Privacy Tsugite (継手, tsugite) checks requests to record consent grants and withdrawals and to query them (ConsentRecord) against the pinned purposes and scopes, carries them to a consent ledger backing service, and matches the returned receipt against the contract. If anything fails, it does not send and does not accept (fail-closed). The consent ledger itself is not included.",
        "problem": "When consent grants and withdrawals are recorded in an external consent ledger, it prevents recording under purposes or scopes that are not pinned, sending the data subject's email address or phone number as is, and mistaking a recorded consent for consent authority. A consent record is a fact, not authority.",
        "use_when": [
          "You want to append the history of consent grants (RECORD_GRANT) and withdrawals (RECORD_WITHDRAWAL) to an external consent ledger backing service.",
          "You want to check the state of recorded consent with a query (QUERY). A query has the side effect NONE.",
          "You want to carry the data subject only as a pseudonymous reference, and not send email-address or phone-number forms to the backing service.",
          "You want to limit consent purposes and scopes to pinned lists (purpose_refs and scope_refs). An empty list permits nothing.",
          "You do not want to trust the receipt from the backing service as is, and want to match it by request_id, request_digest and record_digest before treating consent as recorded."
        ],
        "do_not_use_when": [
          "You want it to judge whether consent is valid. It makes no legal judgements; that is the role of people, organizations and Kernels.",
          "You want to use a recorded consent as consent authority. consent_authority is fixed to NOT_DERIVED or to a person or organization; this Tsugite does not derive it.",
          "You want to delete or rewrite recorded consent. Consent records are append-only, an irreversible external write (EXTERNAL_IRREVERSIBLE). A withdrawal is recorded as RECORD_WITHDRAWAL.",
          "You want a consent ledger you can use right away. No actual backing service (consent ledger) is included; the port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "You want to identify personal information comprehensively. Direct-identifier detection covers only typical forms of email addresses and phone numbers.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for H-03 alone, or want it to handle other H items. This Tsugite covers H-03, H-04, H-05, H-07 and H-09 as one part under the contract, with shared tests and inspection. H-01 and H-08 are served by the Audit Ledger Tsugite, H-06 by the Storage Lifecycle Tsugite (C-08), and H-02 by the audit ledger or an observability sink."
        ],
        "what_it_checks": [
          "It checks the data subject reference and the form of references. A subject reference shaped like an email address or phone number, an empty reference, a reference containing a path or .. (path traversal), and additional fields are not sent.",
          "It checks purposes and scopes. Requests with a purpose or scope that is not pinned are not sent.",
          "It checks that op and side effect correspond. QUERY is NONE; RECORD_GRANT and RECORD_WITHDRAWAL are EXTERNAL_IRREVERSIBLE. Requests and receipts that do not correspond are not sent or accepted.",
          "It checks the receipt's state and record_digest. A state that does not answer the op (for RECORD_GRANT, other than GRANTED or UNKNOWN; for RECORD_WITHDRAWAL, other than WITHDRAWN or UNKNOWN) or a record_digest for another consent (target differs from consent_ref) is not accepted.",
          "A receipt that claims to have derived consent authority is not accepted.",
          "Receipts that increase the external effect, receipts whose effect is UNKNOWN and receipts with a spoofed request_digest are not accepted. When an unacceptable receipt arrives, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved with a lookup."
        ],
        "what_it_does_not_do": [
          "It does not judge whether consent is valid or whether consent authority exists. A consent record is a fact, not authority.",
          "The facts of required consent, missing consent and approval paths are handled by the Kernel (consent-facts, which does not grant consent), the facts of retention by the Kernel (policy-facts), and legal rights by people and organizations. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual backing service (consent ledger). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not identify all personal information. Direct-identifier detection is a net that looks only at typical forms of email addresses and phone numbers.",
          "A recorded consent is not approval (approval: false).",
          "It does not promise compliance with privacy laws. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-privacy/v1. Input: pass a ConsentRecord request to submit(request) on createAuditPrivacyClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and purpose_refs and scope_refs in cfg. Side effect: EXTERNAL_IRREVERSIBLE for records, NONE for QUERY.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-04",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Data subject access request (DSAR) handling (only carries; does not decide) — Audit & Privacy Tsugite",
        "summary": "The Audit & Privacy Tsugite (継手, tsugite) checks a data subject's request (DSAR), carries it to a DSAR intake backing service, and matches the returned receipt against the contract. It only carries the DSAR: it does not decide whether to fulfil it, what to disclose or whether to delete, and carries none of that out. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When a DSAR is carried to an external DSAR intake, it prevents sending the data subject's email address or phone number as is, making an irreversible or unknown effect the intent of the transport, and mistaking a carried request for a judgement of legal entitlement.",
        "use_when": [
          "You want to carry a DSAR to a DSAR intake backing service using only a pseudonymous reference to the data subject.",
          "You want to confirm the transport result only after matching request_id, request_digest and references. The DONE result carries transport_state, dsar_type and legal_entitlement.",
          "You want to keep the side effect of the transport within NONE to EXTERNAL_REVERSIBLE. An irreversible or unknown effect cannot be the intent.",
          "Even if the result becomes unknown because of a backing-service failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want it to judge whether there is a right to have the request fulfilled, or what may be disclosed. It makes no legal judgements; that is the role of people, organizations and Kernels.",
          "You want the intake that fulfils disclosure or deletion requests itself, or a mechanism that carries out disclosure or deletion. This Tsugite only carries the DSAR to the backing service; whether to delete is decided by people and organizations. No actual backing service (DSAR intake) is included either.",
          "You want to use a carried request as a judgement of legal entitlement. legal_entitlement is fixed to NOT_DERIVED or to a person or organization; this Tsugite does not derive it.",
          "You want an irreversible effect to be the intent of carrying a DSAR. Such requests are not sent.",
          "You want to identify personal information comprehensively. Direct-identifier detection covers only typical forms of email addresses and phone numbers.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for H-04 alone, or want it to handle other H items. This Tsugite covers H-03, H-04, H-05, H-07 and H-09 as one part under the contract, with shared tests and inspection. H-01 and H-08 are served by the Audit Ledger Tsugite, H-06 by the Storage Lifecycle Tsugite (C-08), and H-02 by the audit ledger or an observability sink."
        ],
        "what_it_checks": [
          "It checks the data subject reference and the form of references. A subject reference shaped like an email address or phone number, an empty reference, a reference containing a path or .. (path traversal), and additional fields are not sent.",
          "It checks the DSAR intent. Requests whose intent is an irreversible or unknown effect are not sent.",
          "A receipt that claims to have judged legal entitlement is not accepted.",
          "If the receipt's request_id, request_digest or references do not match the request, it is not accepted.",
          "Receipts that increase the external effect, receipts whose effect is UNKNOWN and receipts with a spoofed request_digest are not accepted. When an unacceptable receipt arrives, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved with a lookup."
        ],
        "what_it_does_not_do": [
          "It makes no legal judgements. Whether there is a right to have the request fulfilled, and what may be disclosed, are the role of people, organizations and Kernels. The Kernel (consent-facts) handles the facts of required and missing consent; it does not grant consent.",
          "It does not decide on disclosure or deletion, and does not carry them out. It only carries the DSAR; whether to delete is decided by people and organizations. Carrying a DSAR is not a judgement of legal entitlement.",
          "It does not include an actual backing service (DSAR intake). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not identify all personal information. Direct-identifier detection is a net that looks only at typical forms of email addresses and phone numbers.",
          "A carried request is not approval (approval: false).",
          "It does not promise compliance with privacy laws. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-privacy/v1. Input: pass a DSAR request to submit(request) on createAuditPrivacyClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries transport_state, dsar_type and legal_entitlement in its result. Every result has approval: false. User provides: a port to the DSAR intake backing service (submit, and optionally lookup). Side effect: NONE to EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-05",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "PII detection and redaction pipeline — Audit & Privacy Tsugite",
        "summary": "The Audit & Privacy Tsugite (継手, tsugite) checks redaction requests (RedactionPipeline) against the pinned redaction definitions, carries them to a redaction backing service, and matches the returned receipt against the contract. It does not do the redaction itself, decide what to redact, or confirm that the redaction is complete. If anything fails, it does not send and does not accept (fail-closed).",
        "problem": "When redaction is delegated to an external backing service, it prevents processing with unpinned definitions, accepting a result that overwrote the input or a redaction with an external effect, treating a partially redacted result (PARTIAL) as fully redacted, and mistaking redaction for disclosure authorization.",
        "use_when": [
          "You want to carry redaction processing to the backing service, limited to pinned redaction definitions (redaction_manifests).",
          "You want to receive the redaction result under a reference separate from the input (output_ref), and not let the input be overwritten.",
          "You want to distinguish a partially redacted result (PARTIAL) from a fully redacted one. For PARTIAL, fully_redacted is false.",
          "You want to keep the side effect of redaction to NONE or a locally reversible effect (LOCAL_REVERSIBLE). The DONE result carries redaction_state, fully_redacted, output_ref and disclosure_authorization."
        ],
        "do_not_use_when": [
          "You want it to decide what to redact. That decision is the role of the Kernel (redact-facts).",
          "You want to check whether the redaction is correct (nothing missed). This Tsugite looks only at the receipt's state and the output reference.",
          "You want comprehensive detection of sensitive information. Direct-identifier detection is a net that looks only at typical forms of email addresses and phone numbers placed in data subject references; it does not identify all personal information.",
          "You want to use redaction as disclosure authorization. disclosure_authorization is fixed to NOT_DERIVED or to a person or organization; this Tsugite does not derive it.",
          "You want a redaction backing service you can use right away. No actual backing service is included; the port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for H-05 alone, or want it to handle other H items. This Tsugite covers H-03, H-04, H-05, H-07 and H-09 as one part under the contract, with shared tests and inspection. H-01 and H-08 are served by the Audit Ledger Tsugite, H-06 by the Storage Lifecycle Tsugite (C-08), and H-02 by the audit ledger or an observability sink."
        ],
        "what_it_checks": [
          "It checks the redaction definition. Requests with a definition (redaction manifest) that is not pinned are not sent.",
          "A receipt that overwrote the input (the output has the same reference as the input) is not accepted.",
          "A redaction receipt with an external effect is not accepted. The side effect of redaction is NONE or LOCAL_REVERSIBLE, and FAILED is NONE.",
          "A receipt that claims to have derived disclosure authorization is not accepted.",
          "It does not treat PARTIAL as fully redacted (fully_redacted: false).",
          "It checks the data subject reference and the form of references. A subject reference shaped like an email address or phone number, an empty reference, a reference containing a path or .. (path traversal), and additional fields are not sent.",
          "Receipts that increase the external effect, receipts whose effect is UNKNOWN and receipts with a spoofed request_digest are not accepted. When an unacceptable receipt arrives, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved with a lookup."
        ],
        "what_it_does_not_do": [
          "It does not do the redaction itself. Redaction processing is the backing service's role; this Tsugite only checks and carries requests and matches receipts. No actual backing service (redaction processing) is included; the port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not decide what to redact. That is the role of the Kernel (redact-facts).",
          "It does not check whether the redaction is correct (nothing missed). It looks only at the receipt's state and the output reference.",
          "It does not identify all personal information. Direct-identifier detection is a net that looks only at typical forms of email addresses and phone numbers.",
          "Redaction is not disclosure authorization. A completed redaction is not approval either (approval: false).",
          "It does not promise compliance with privacy laws. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-privacy/v1. Input: pass a RedactionPipeline request to submit(request) on createAuditPrivacyClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and redaction_manifests in cfg. Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-06",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Retention enforcement — Storage Lifecycle Tsugite",
        "summary": "The Storage Lifecycle Tsugite (継手, tsugite) checks retention requests (RetentionRequest: EVALUATE, RETAIN or EXPIRE) against the pinned retention policies and logical targets before sending them to the storage backend, and checks the receipt against the contract. Irreversible deletion (EXPIRE) is permitted by the allow_expire setting (default false) and is sent only for targets without a legal hold (fail-closed).",
        "problem": "When requests for deletion or preservation at expiry are sent to the backend, it prevents sending deletions that are not permitted or deletions of targets under legal hold, treating an expiry evaluation alone as a deletion, and treating data as deleted without checking the backend's receipt.",
        "use_when": [
          "You want to check retention requests (RetentionRequest) that follow a retention policy and then carry them to the storage backend.",
          "You want to send deletion (EXPIRE) requests only when the allow_expire setting permits it.",
          "You want to receive the fact of a legal hold (a Kernel or human decision) and stop sending EXPIRE for that target from then on.",
          "You want to ask the backend only for an evaluation of whether data has expired (EVALUATE), without deleting.",
          "You want to treat data as deleted (deleted: true) only when the EXPIRE receipt is matched as EXPIRED."
        ],
        "do_not_use_when": [
          "You want the mechanism itself that deletes or preserves data automatically when it expires. Executing deletion and preservation at expiry is the role of the underlying platform; this Tsugite only carries the retention requests it is given and matches the receipts.",
          "You want this Tsugite to decide retention periods or legal holds. Those decisions belong to Kernels and people; this Tsugite only receives the facts and keeps to them.",
          "You want to move data to another tier on expiry. Moving between tiers is an Archive request, the C-09 facet.",
          "You want a store you can use right away. No actual backend is included, and which storage service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "When the allow_expire setting is not true, it does not send EXPIRE.",
          "It does not send EXPIRE for a target under legal hold.",
          "An EVALUATE receipt that involves deletion is not accepted. EXPIRED for EVALUATE is only an expiry evaluation, not a deletion.",
          "A receipt that returns EXPIRED for RETAIN is treated as an anomaly and not accepted.",
          "Requests for a retention policy that is not in the pinned list are not sent.",
          "It checks the request's type. Requests with additional fields, values that differ from the fixed values (const), file-system targets, an unpinned backend or a target that is not allowed are not sent.",
          "It checks the receipt. A spoofed request_digest, a receipt for another target, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "FAILED, REFUSED and SIMULATED receipts are not accepted unless they have no effect (NONE). A backend failure is treated as UNKNOWN, and the same request_id is never sent twice."
        ],
        "what_it_does_not_do": [
          "It does not execute deletion or preservation at expiry. Execution is the role of the underlying platform; this Tsugite only carries retention requests and matches the receipts.",
          "It does not decide retention periods or legal holds. That is the role of the Kernels (redact-facts and policy-facts) and people; this Tsugite only receives those facts and keeps to them.",
          "A completed deletion is not approval (approval: false).",
          "It does not include an actual backend. Which storage service to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "It is not a part dedicated to H-06. The same single part serves C-05 to C-09 and F-04, and H-06 is the retention (RetentionRequest) facet of C-08. The tests and inspection are shared.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/storage-persistence/v1. Input: pass a RetentionRequest to submit(request) on createStorageLifecycleClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries decision, deleted and effect. Every result has approval: false. User provides: a storage backend port (submit, and optionally lookup) and settings for the retention policies and allow_expire. Side effect: EXTERNAL_IRREVERSIBLE for EXPIRE, NONE otherwise.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-07",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Legal hold (only carries; neither decides nor freezes) — Audit & Privacy Tsugite",
        "summary": "The Audit & Privacy Tsugite (継手, tsugite) checks legal hold (LegalHold) requests to place (PLACE) and release (RELEASE) a hold against the pinned scopes, only when there is a reference to evidence received from a person or organization, then carries them to a legal hold backing service and matches the receipt. It only carries requests: it neither freezes deletion itself nor judges whether a hold is needed. If anything fails, it does not send and does not accept (fail-closed).",
        "problem": "When a legal hold is carried to an external mechanism, it prevents carrying it without evidence from a person or organization, carrying it on evidence whose origin is not a person or organization or is ERABAZU itself, and mistaking a carried request for legal hold authority.",
        "use_when": [
          "You want to carry place and release requests to the backing service with a reference to legal hold evidence issued by a person or organization. Evidence is received with recordAuthorityEvidence(evidence_ref, origin) (origin is HUMAN or ORGANIZATION only).",
          "You want to limit the scope of a legal hold to a pinned list (scope_refs).",
          "You want to treat a request as carried only after matching that the receipt's state answers the op (PLACE→PLACED, RELEASE→RELEASED) or is FAILED. The DONE result carries hold_state and legal_hold_authority.",
          "Even if the result becomes unknown because of a backing-service failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want it to judge whether a legal hold is needed. It makes no legal judgements; that is the role of people, organizations and Kernels.",
          "You want to check whether the evidence is a genuine decision. It does not check the content of the evidence, only whether it is a reference received from a person or organization.",
          "You want to use a carried request as legal hold authority. legal_hold_authority is fixed to NOT_DERIVED or to a person or organization; this Tsugite does not derive it.",
          "You want the mechanism that freezes deletion itself. This Tsugite only carries requests; no actual backing service (legal hold mechanism) is included, and the port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "There is no evidence from a person or organization yet. Requests without evidence are not sent (REFUSED).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for H-07 alone, or want it to handle other H items. This Tsugite covers H-03, H-04, H-05, H-07 and H-09 as one part under the contract, with shared tests and inspection. H-01 and H-08 are served by the Audit Ledger Tsugite, H-06 by the Storage Lifecycle Tsugite (C-08), and H-02 by the audit ledger or an observability sink."
        ],
        "what_it_checks": [
          "It checks that evidence exists. Requests without evidence from a person or organization are not sent (REFUSED).",
          "It checks the origin of the evidence. It does not carry requests on evidence from anything other than a person or organization (HUMAN, ORGANIZATION), or on evidence whose origin is ERABAZU.",
          "It checks the scope. Requests for a scope that is not pinned are not sent.",
          "A receipt that claims legal hold authority is not accepted.",
          "It checks the receipt's state. A state that does not answer the op (none of PLACE→PLACED, RELEASE→RELEASED or FAILED) is not accepted.",
          "It checks the data subject reference and the form of references. A subject reference shaped like an email address or phone number, an empty reference, a reference containing a path or .. (path traversal), and additional fields are not sent.",
          "Receipts that increase the external effect, receipts whose effect is UNKNOWN and receipts with a spoofed request_digest are not accepted. When an unacceptable receipt arrives, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved with a lookup."
        ],
        "what_it_does_not_do": [
          "It does not judge whether a legal hold is needed. Legal judgements are the role of people, organizations and Kernels.",
          "It does not create legal hold evidence (ERABAZU does not create evidence). It does not check the content of the evidence (whether it is a genuine decision) either, only whether it is a reference received from a person or organization.",
          "It does not freeze deletion (preservation) itself. It only carries requests to the backing service and matches the receipt. No actual backing service is included.",
          "Handling the facts of retention and legal hold policies is the role of the Kernel (policy-facts), and authority is the role of people and organizations. This Tsugite only checks before sending and after receiving.",
          "A carried request is not legal hold authority and is not approval (approval: false).",
          "It does not promise compliance with privacy laws. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-privacy/v1. Input: after receiving the evidence with recordAuthorityEvidence(evidence_ref, origin) on createAuditPrivacyClient({ cfg, port }), pass a LegalHold request to submit(request). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and scope_refs in cfg. Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-08",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Tamper-detection chain — Audit Ledger Tsugite",
        "summary": "The Audit Ledger Tsugite (継手, tsugite) handles a tamper-evident chain that links audit event envelopes (AuditEventEnvelope) by the previous hash (previous_hash). It checks the chain and the digest against the contract's formulas before sending to an append-only backend, and then matches the chain hash in the receipt the backend returns (AuditAppendReceipt). If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "A hash-linked audit chain loses its power to reveal tampering if a previous hash is rewritten somewhere or the digest is computed in a different way. This Tsugite checks both the envelope before sending and the store's receipt with the same contract formulas, and does not send or accept a chain that does not fit them, so a chain that cannot be matched is never trusted.",
        "use_when": [
          "You want to keep audit events on an external append-only backend as a tamper-evident chain linked by the previous hash.",
          "You want chain hashes computed and matched with one fixed formula (a restricted form of RFC 8785 canonicalization and sha256).",
          "You do not want to trust the event_digest or chain hash in the store's receipt as is, and want to recompute them from the envelope that was sent and match them before treating the event as recorded.",
          "You want appending to stop until a person checks it when a chain mismatch is found.",
          "You want to try only the envelope check and the receipt match without calling a backend. The pure functions checkEnvelope and checkReceipt return { ok, errors }."
        ],
        "do_not_use_when": [
          "You need a transparency log (Merkle inclusion proofs). This Tsugite handles a chain linked by the previous hash; inclusion proofs are out of scope.",
          "You want to leave the final judgement on whether tampering occurred to this Tsugite. Deciding whether records are unalterable is the role of the Kernels (provenance-facts and integrity-facts).",
          "You want to use a hash formula other than restricted RFC 8785 canonicalization with sha256. Hashes computed with a different formula are not accepted.",
          "You want to put the body of audit events itself on the chain. The envelope carries only the contract's fields, and a body mixed into it is rejected.",
          "You want a store you can use right away. No actual backend is included, and which append-only service to connect to has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the previous hash (previous_hash) against the head of the ledger. An envelope whose previous_hash has been rewritten is not sent and not accepted.",
          "It recomputes chain hashes with the contract's formula: restricted RFC 8785 canonicalization and sha256. A hash computed over a non-canonicalized form is not accepted.",
          "It checks the rules for the start of the chain (genesis). For genesis, previous_hash_value is computed as the empty string, and envelopes that violate the genesis rules are not sent.",
          "It checks the digest. A digest computed over the wrong target (domain) is not accepted.",
          "It checks the receipt (AuditAppendReceipt). If event_digest is spoofed, the chain hash is wrong, or the ledger, sequence, event_id or target does not match, the receipt is not accepted (RECEIPT_REJECTED).",
          "If a receipt is inconsistent, it stops (HALTED) and does not append until a person checks it.",
          "If the sequence rolls back or skips, it does not send and does not accept. Appends that break the order of the chain do not pass."
        ],
        "what_it_does_not_do": [
          "Deciding whether records are unalterable, and whether tampering occurred, is the role of the Kernels (provenance-facts and integrity-facts). This Tsugite only checks before sending and matches the receipt.",
          "It does not handle transparency logs (Merkle inclusion proofs).",
          "It does not include an actual backend. Which append-only service to connect to, the port implementation and what to pin (endpoint and receipt format) are undecided and will be settled at a separate gate when connecting.",
          "It does not resolve the secret (SecretRef) used for backend authentication. That is the responsibility of the port.",
          "A successful append is not approval (approval: false).",
          "It does not change the contract or the hash formula. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "It is not a part dedicated to H-08. The same single part serves H-01 (append-only audit log), C-04 (append-only ledger) and H-02 (access log), and the tests and inspection are shared.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-ledger/v1. Input: pass an AuditEventEnvelope to append(envelope) on createLedgerClient({ cfg, port }). Returns: one of APPENDED_VERIFIED, ALREADY_APPENDED, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. For checks only, checkEnvelope and checkReceipt return { ok, errors }. User provides: a port to the append-only backend (append, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE (a write that cannot be undone).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-09",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Regulatory report output (only carries; not regulator acceptance) — Audit & Privacy Tsugite",
        "summary": "The Audit & Privacy Tsugite (継手, tsugite) checks regulatory report (RegulatoryReport) requests and the report content (bytes) against the pinned report formats and destinations, the digest and the size limit, then carries them to a regulator submission backing service and matches the receipt. It only carries reports: the user passes the report content, and delivering a report is not acceptance by the regulator. If anything fails, it does not send and does not accept (fail-closed).",
        "problem": "When a regulatory report is carried to an external submission point, it prevents sending to a format or destination that is not pinned, sending altered or oversized content, making an irreversible submission that is not permitted, and mistaking delivery for acceptance by the regulator.",
        "use_when": [
          "You want to limit report formats and destinations to pinned lists (report_profiles and report_destinations).",
          "You want to check the digest of the report content (bytes) and the size limit (max_report_bytes) before carrying it. max_report_bytes is required when reports are used.",
          "You want irreversible submissions only when the allow_irreversible setting is true (default false).",
          "You want to tell generated (GENERATED) from delivered (DELIVERED) by checking against the side effect. GENERATED is NONE or LOCAL_REVERSIBLE; DELIVERED is EXTERNAL_*. The DONE result carries delivery_state, delivered and regulatory_acceptance."
        ],
        "do_not_use_when": [
          "You want to know whether the regulator accepted the report. A delivered report is not acceptance by the regulator; regulatory_acceptance is fixed to NOT_DERIVED or to a person or organization, and this Tsugite does not derive it.",
          "You want the receipt to confirm that the report reached the pinned destination. Regulatory report receipts have no destination field; only the pin before sending can be checked.",
          "You want it to create the report content. The user passes the content as bytes in submit(request, bytes); this Tsugite checks only the digest and the size limit.",
          "You want it to make legal judgements. It makes no legal judgements; that is the role of people, organizations and Kernels.",
          "You want a submission point you can use right away. No actual backing service (regulator submission point) is included; the port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a part for H-09 alone, or want it to handle other H items. This Tsugite covers H-03, H-04, H-05, H-07 and H-09 as one part under the contract, with shared tests and inspection. H-01 and H-08 are served by the Audit Ledger Tsugite, H-06 by the Storage Lifecycle Tsugite (C-08), and H-02 by the audit ledger or an observability sink."
        ],
        "what_it_checks": [
          "It checks the report format and destination. Requests with a format (report profile) or destination (report destination) that is not pinned are not sent.",
          "It checks the report content. Content whose bytes do not match the digest, or that exceeds the limit, is not sent.",
          "It checks irreversible submissions. They are not sent unless allow_irreversible is true.",
          "It checks that state and side effect correspond. A GENERATED receipt that claims an external effect, or a DELIVERED receipt without an effect, is not accepted.",
          "A receipt that claims to have derived acceptance by the regulator is not accepted.",
          "It checks the data subject reference and the form of references. A subject reference shaped like an email address or phone number, an empty reference, a reference containing a path or .. (path traversal), and additional fields are not sent.",
          "Receipts that increase the external effect, receipts whose effect is UNKNOWN and receipts with a spoofed request_digest are not accepted. When an unacceptable receipt arrives, it stops (HALTED).",
          "The same request_id is never sent twice. A backing-service failure is treated as UNKNOWN, and a resend is resolved with a lookup."
        ],
        "what_it_does_not_do": [
          "It does not derive acceptance by the regulator. Delivery is not acceptance, and it is not approval either (approval: false).",
          "It does not use the receipt to confirm where the report arrived. Regulatory report receipts have no destination field; it checks only by the pin before sending.",
          "For the report content, this Tsugite checks only the digest and the size limit. It makes no legal judgements (the role of people, organizations and Kernels).",
          "Legal rights and disclosure authorization are the role of people and organizations. This Tsugite only checks before sending and after receiving.",
          "It does not include an actual backing service (regulator submission point). The port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not promise compliance with privacy laws. It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/audit-privacy/v1. Input: pass a RegulatoryReport request and the content bytes to submit(request, bytes) on createAuditPrivacyClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the backing service (submit, and optionally lookup) and cfg (the pinned formats and destinations, max_report_bytes and allow_irreversible). Side effect: NONE to EXTERNAL_*.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-10",
        "category": "H/監査・統制・プライバシー",
        "kind": "Primitive",
        "title": "Lineage record — Lineage record part (Primitive)",
        "summary": "The Lineage record part (Primitive) is a standalone part that creates a lineage record (erabazu.lineage/1) from the set of inputs used in an aggregation (a list of Refs), the reference and version of the aggregator, and the output bytes, and later recomputes from the inputs and output to check against the record. The order of the inputs does not change the result, and duplicates are refused rather than silently removed. Differences found when checking are not errors; they are returned as a list of the fields that differed. It does not run the aggregator.",
        "problem": "It provides backing when the origin of a number is questioned later. It keeps which inputs were used, with which version of the aggregator, and which output was produced, as the hash of the input set, the aggregator version and the hash of the output. Because it does not silently remove duplicate inputs, it does not hide a miscount of which inputs were used.",
        "use_when": [
          "You want to create a lineage record from the input set of an aggregation, the aggregator's reference and version, the output bytes and the recording time (recordLineage).",
          "You want a digest of the input set that does not depend on the order of the inputs. It is the sha256 of the RFC 8785 canonical JSON of the array of inputs sorted in UTF-16 code unit order.",
          "You want to recompute later from the inputs and output and confirm that they are the same as the record (checkLineage; a difference is matches: false with the mismatched list, and is not an error).",
          "You want to know which fields differed (input_set.count, input_set.digest, output.digest, output.size).",
          "You want to call it locally as deterministic processing that has no clock, no random numbers and no outside connection. recorded_at is passed by the caller."
        ],
        "do_not_use_when": [
          "You need proof that the aggregator is correct or that the output is correct. This part does not run the aggregator; the lineage is a record of what was used.",
          "You want to confirm that it really ran with that version or at that time. The tool's ref and version and recorded_at are recorded exactly as passed, and checking only checks their form and does not compare their values.",
          "You want to confirm that the Refs in inputs point to inputs that exist, or what they contain. It checks only the form of a Ref; even if the content of an input changes, the digest is the same as long as the Ref is the same.",
          "You want to stop tampering with the record itself. That is the role of signing (B-12) or the seal (H-11).",
          "You want to record with a time that has an offset (+09:00 and so on) or a leap second. recorded_at is UTC Z only; anything else stops with INVALID_TIME.",
          "You handle more than 1,048,576 inputs. It stops with INVALID_INPUTS.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the same string appears in inputs two or more times, it stops with DUPLICATE_INPUT rather than silently removing it (it compares by exact string match; uppercase and lowercase are different inputs).",
          "It stops with INVALID_INPUTS when inputs is not a plain array, has holes, extra properties or getter elements, has elements that are not Refs, or has more than 1,048,576 items.",
          "It stops with INVALID_TOOL when tool_ref is not a Ref, or tool_version is not 1 to 200 characters or contains control characters or unpaired surrogates, and with NOT_BYTES when output_bytes is not a Uint8Array.",
          "It stops with INVALID_TIME when recorded_at is not in RFC 3339 UTC (Z) form, or is a date and time that does not exist (February 30, hour 24, second 60 and so on).",
          "When the record given to checkLineage has the wrong form (missing or extra fields, a different schema, a negative count, a digest that is not 64 lowercase hex digits and so on), it stops with INVALID_RECORD and the name of the first field found (field).",
          "It stops with INVALID_ARGUMENT when the argument is not a plain object or has fields other than the fixed ones, and with INSPECTION_FAILED when reading the argument itself raises an exception. It reads each field only once."
        ],
        "what_it_does_not_do": [
          "A lineage is a record of \"what was used and what was produced\"; it is not proof that the aggregator is correct, that the output is correct, or that the record has not been tampered with. matches: true shows only that the inputs and output passed were the same as the record.",
          "It does not store records. It has no state, no clock and no outside connection.",
          "It creates a record even with 0 inputs (count 0; the digest is the sha256 of \"[]\"). It does not refuse 0 inputs.",
          "The SHA-256 computation itself is left to the Node.js crypto module, and the fuzz oracle uses the same crypto.",
          "Canonicalization uses a copy of the I-08 part's code (src/canonical.js). The branches inside the copy are not themselves exercised by this part's tests (the copy's tests are on the I-08 side).",
          "Records and check results are not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not an implementation of a particular provenance specification."
        ],
        "integration": "Call recordLineage({ inputs, tool_ref, tool_version, output_bytes, recorded_at }) and checkLineage({ record, inputs, output_bytes }) in src/lineage.js (they do not throw). A record is schema (erabazu.lineage/1), input_set (count, digest), tool (ref, version), output (digest, size) and recorded_at. It returns record, or matches and mismatched; when it stops, it returns { ok: false, errors: [{ code }] }. There are no side effects, no state, no outside connection and no credentials, and there is no port for the caller to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-11",
        "category": "H/監査・統制・プライバシー",
        "kind": "Component",
        "title": "Seal (append-only signing and daily roots) — Seal (append-only signing and daily roots) part (Component)",
        "summary": "The Seal part (Component) is a part that bundles the Merkle tree (the B-11 part (Primitive)), signing with a local key (the B-12 part (Primitive)), the idempotent write Tsugite (C-05) and an optional anchor port (in the shape of H-01). appendSeal checks the chain of earlier seal records passed in by the caller and writes a record that signs the new root including the added files, as a separate object with the next seq. finalizeDay checks the chain of each partition, builds the day's root, writes seal/roots/<date>.json and a signed day record, and anchors it if there is an anchor port. No result is approval or a time authority.",
        "problem": "It is a part for backing up that raw data has not been rewritten afterwards. It prevents writing on after a rewrite that was not re-signed or after a record from an untrusted key, trusting a proof or the tree size stated by the caller, overwriting a record, and claiming to have anchored when anchoring did not succeed.",
        "use_when": [
          "For each set of files added to a partition (folder_ref) (name and sha256, 1 to 10000 files), you want to write a signed seal record that follows the chain of earlier records, once, at <folder_ref>/seal/<seq>.json (appendSeal).",
          "For each day, you want to build the day's root from the partition roots in folder_ref order, and write a file in the shape canonical({ seal_date, root }) and a signed day record (finalizeDay). The day record contains no clock value, so the same chain gives the same bytes.",
          "You want to anchor the day's root externally through the injected anchor port (the same shape as append of H-01) and keep its receipt at seal/roots/<date>.anchor.json. The anchor port is called only after the root file and the day record have been written.",
          "You want to check a file's inclusion proof against one signed record (verifyFile), or to build that proof from the chain (proveFile).",
          "You want to leave signing to a sign port that encloses the private key, without passing the private key to this part. Only the first key in keys signs; the other keys are used only to check earlier records.",
          "You want to resend a write or anchoring whose result is unknown with the same request, the same bytes and the same envelope (resolvePending)."
        ],
        "do_not_use_when": [
          "You want to use it, without an anchor port, as evidence that something \"existed at this date and time\". Without an anchor port, anchored is false and the record only says \"this is what was signed\". sealed_at is the value of the injected clock and is not a time authority (trusted time is B-06).",
          "You want to use the signature as proof that the file content or the fetch is correct, as a business decision, or as approval. The signature only means \"the holder of a key in keys signed these bytes\".",
          "You want it to also handle where keys are kept, their storage, rotation and revocation. Which key to trust and until when is not decided in this part; that is outside it.",
          "You want verifyFile alone to also check the chain. verifyFile checks only the signature and inclusion of one record, and cannot detect a record signed with a trusted key over a wrong tree_size. The chain is checked in appendSeal, finalizeDay and proveFile.",
          "You want this part to read from storage. This part does not read storage; the caller reads the chain from storage and passes it in. If it is passed a version of a record that is not in storage, it writes a record following it, and from then on that partition stops with SEAL_CHAIN_BROKEN (a person fixes it).",
          "You want to receive a retry of finalizeDay with an anchor port as a success. The anchor record contains anchor_status, so its content changes, and storage refuses it, ending with WRITE_REFUSED.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, keys does not have 1 to 16 entries, a key_id or fingerprint is duplicated, or a port function is missing, it stops with INVALID_OPTIONS; when the input to an operation is malformed, with INVALID_INPUT; and when a getter throws an exception, with INSPECTION_FAILED.",
          "When the chain passed in does not pass the check (canonicalization, fields, schema, folder_ref, seq, duplicate names, tree_size, prev_entry_sha256, the signature by a trusted key, consistency with the previous root, the root recomputed from the files), it writes nothing (SEAL_CHAIN_BROKEN). errors[0] carries folder_ref, at and reason.",
          "It takes tree_size and the root again from the signed records and the number of files. It does not use consistency or inclusion proofs or the size stated by the caller (RFC 9162 proofs do not bind the size of the tree).",
          "When the names of the files to add are duplicated or already in the chain, it stops with DUPLICATE_NAME; when the same folder_ref appears twice in finalizeDay, with DUPLICATE_FOLDER; and when a partition exceeds 10000 records or 100000 files, with SEAL_LIMIT_EXCEEDED.",
          "When the sign port throws or is not ok, it reports SIGN_FAILED; when the result is not Ed25519 with the first key's key_id and fingerprint, or the round-trip check with the first key's public key fails, SIGNER_MISMATCH; and on a fault or malformed result from the B-11 or B-12 port, MERKLE_PORT_FAILED or VERIFY_PORT_FAILED; and it does not write.",
          "When the result of a write is unknown, it reports WRITE_UNKNOWN; when the Tsugite did not send, WRITE_REFUSED; and when the Tsugite halted or the result is not APPLIED or DEDUP_HIT, WRITE_FAILED. When the result of the anchor port is unknown, it reports ANCHOR_UNKNOWN; when it refused, ANCHOR_REFUSED; and on a halt, a failed receipt match or a malformed result, ANCHOR_FAILED; and it does not set anchored to true. While something is pending, appendSeal and finalizeDay return SEAL_PENDING.",
          "In verifyFile, when the record cannot be canonicalized, is malformed, or is not signed by a trusted key, it reports ENTRY_UNVERIFIED, and when the proof's tree_size differs from the record or leaf_index is too large, PROOF_NOT_FOR_ENTRY; in proveFile, when the name is not in the chain, NAME_NOT_SEALED.",
          "When timestamp does not return an existing date-time in RFC 3339 UTC (Z), it reports CLOCK_FAILED; when the previous operation has not finished, BUSY; and calling resolvePending when nothing is pending returns NOTHING_PENDING."
        ],
        "what_it_does_not_do": [
          "The Seal part is not a Tsugite. It has no Request or Receipt of its own; writing is caused by the bundled C-05 Tsugite under its own contract, and anchoring by the injected port under that port's contract (appending to a ledger and a Git push are effects that cannot be undone). This part itself does not touch the outside.",
          "The meaning of the bundled parts (the root is correct ≠ the content is correct, signature ≠ approval, anchored ≠ stamped with an authoritative time) stays on each part's side.",
          "No result is approval, authority or a time authority (approval: false, authority: NOT_DERIVED, timestamp_authority: NOT_DERIVED, derives_authority: false).",
          "The strength of the external anchoring goes only as far as the strength of the injected anchor port and what lies beyond it. This part only records that the port said it anchored, and the receipt; it does not check the anchor target itself.",
          "It does not send overwrite or delete requests. It does not see the private key (it stays inside the sign port).",
          "The unit tests and fuzzing use stand-ins (the signing stand-in in fuzzing is not Ed25519 but a SHA-512 stand-in, for speed). The wiring with the real B-11, B-12, C-05 and H-01 was checked by a separate wiring check. For the Git anchor, only the success path was checked in the wiring; the refusal, conflict and non-fast-forward paths have not been tried.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular transparency log or signing product."
        ],
        "integration": "Create it with createSeal(options) in src/seal.js; it returns { ok, errors, instance }. options are target, request_prefix, keys, ports and an optional timestamp. The operations are appendSeal, finalizeDay, verifyFile, proveFile, resolvePending and status, and the results carry approval: false. Ports: merkle (the 5 functions of B-11), sign (a closure that encloses the private key), verify and publicKeyFingerprint (B-12), write.submit (the client of the C-05 Tsugite) and an optional anchor.append. Credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "H-12",
        "category": "H/監査・統制・プライバシー",
        "kind": "Tsugite",
        "title": "Anchoring seal roots in Git — Seal Root Git Anchor Tsugite",
        "summary": "The Seal Root Git Anchor Tsugite (継手, tsugite) checks a request (SealAnchorRequest) to commit the daily seal root file (seal/roots/YYYY-MM-DD.json) to a fixed Git branch as a child of the previous version (parent_commit) and push it as a fast-forward, carries it to the port, and matches the returned receipt (SealAnchorReceipt). It only appends; when a check fails, it does not send and does not accept (fail-closed).",
        "problem": "It makes it possible to check from the outside \"as of when the root was fixed\". In doing so, it prevents overwriting a root, anchoring a different root for the same date, committing twice while the result of a push is not visible, and mistaking the commit date for a time authority.",
        "use_when": [
          "You want to anchor the daily root file written by the seal part in a Git repository, by appending only.",
          "Before sending, you want to rebuild the file content canonical({seal_date, root}) from the request's root_digest and confirm that its sha256 is the same as file_digest.",
          "You want to keep one root per date. For a date in conflict (CONFLICT), nothing is sent until a person decides.",
          "When the result of a push is not visible, you want to resolve it by reading the other side (the head of the branch and the file at the path) before sending again (port.inspect with SealInspectRequest / SealInspectReceipt)."
        ],
        "do_not_use_when": [
          "You want to create or verify the root itself (the Merkle computation and the signature). That is the role of the seal part; this Tsugite only rebuilds the file digest from the root digest and compares it.",
          "You want to stamp with a trusted time. Both the commit date and the host's push time are observations. A trusted time is stamped separately with the B-06 (RFC 3161) Tsugite.",
          "You want to rewrite history (overwrite, force-push, amend or delete). Neither the request nor the settings have such fields.",
          "You want a template for using the API of a specific Git hosting service (GitHub and the like) or git commands. This Tsugite is not such a template.",
          "You want an anchoring target you can use right away. No commit or push to an actual Git repository or remote has been made (0 times); the port implementation, the credentials and the branch protection settings will be decided at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the settings and the request. repository, branch and committer must be in the setting lists (repositories, branches and committer_refs). Only a branch_ref that conforms to git check-ref-format is allowed, and a seal_date that does not exist in the calendar is not sent. An empty list allows nothing.",
          "It compares the file digest. It does not send a request whose rebuilt sha256 differs from file_digest. It does not accept a receipt whose committed_file_digest differs, a receipt whose ANCHORED parent_commit differs from the request, or a receipt whose changed_paths is not exactly [path].",
          "It keeps the anchoring append-only. It does not accept a receipt whose history_rewrite is not NOT_PERFORMED. If the settings contain an unknown key (such as force_push), it sends nothing.",
          "It matches the state of each date. It does not send a different root for a date it knows. It does not accept a second ANCHORED for a date it knows, or an ALREADY_ANCHORED with a root different from the one it knows, and it stops (HALTED).",
          "It matches the length of the commit ID. It accepts a commit_id only when it has the same number of hex digits as the request's parent_commit (40 or 64, depending on the repository format).",
          "It does not accept a receipt that escalates the effect, a receipt other than FAILED whose effect is unknown, a receipt outside the state table, or a receipt for a different request, and it stops. The effect of ANCHORED is EXTERNAL_IRREVERSIBLE; for the others it is NONE (for FAILED, NONE or UNKNOWN).",
          "A push whose result is not visible (a port failure, or a FAILED or UNKNOWN receipt) is treated as UNKNOWN (unresolved), and is treated neither as a success nor as a confirmed failure. When it cannot read the other side, it does not send again, and it sends no other request until the push is resolved.",
          "Push credentials are only references (committer_ref). A configuration in which committer_refs contains a reference to a seal signing key (seal_signing_key_refs) is rejected as an error."
        ],
        "what_it_does_not_do": [
          "It does not check whether the commit content really has that parent and that change, whether the push was a fast-forward, or whether an answer it read reflects the real Git state (these are what the port reports; this Tsugite does not read Git objects).",
          "The state of each date exists only inside this Tsugite and is lost on restart. It cannot detect an overwrite of a date this Tsugite does not know. The ban on overwrites and force-pushes is enforced separately by branch protection on the repository.",
          "It does not check whether the commit date or host_time is correct, nor their order. Neither is a time authority (commit_date_authority and timestamp_authority are NOT_DERIVED).",
          "It does not check whether the root itself is correct. Resolving a CONFLICT is a decision for a person.",
          "Having anchored a root is not approval, not a stamp with an authoritative time, and does not mean the root is correct (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/seal-anchor-git/v1 (SealAnchorRequest / SealAnchorReceipt, with SealInspectRequest / SealInspectReceipt for reading). Input: pass a SealAnchorRequest to submit(request) on createSealAnchorClient({ cfg, port, clock }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: a Git port (submit, and optionally lookup and inspect) and the lists in cfg. Side effect: EXTERNAL_IRREVERSIBLE for ANCHORED only.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-01",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Input schema validation — Input schema validation part (Primitive)",
        "summary": "The Input schema validation part (Primitive) is a standalone part that checks a JSON value not against the whole of JSON Schema 2020-12 but against a fixed subset (profile json-schema-subset/v1). Only 28 fixed keywords can be used, and any other keyword (including format) causes the whole schema to be refused (it is not silently skipped). The result is { ok, errors }, and when it stops it returns the code and position (JSON Pointer) of the first error only (fail-closed).",
        "problem": "When checking that input has a valid shape, it prevents unknown keywords, external $refs and broken values from being silently skipped and treated as \"passed\". It fixes the keywords that can be used and refuses the whole schema for anything outside the subset.",
        "use_when": [
          "You want to check the shape of a JSON value (type, required, properties, additionalProperties, lengths, ranges, pattern, enum, const and so on) with a fixed subset of keywords (validate).",
          "You want to confirm first, without an instance, that the schema itself fits within the subset (checkSchema).",
          "You want to receive the reason it stopped as the first error, with its code, instance_path and schema_path (JSON Pointers), in a fixed order.",
          "You want the check to be done on a copy read once from the schema and the instance, without calling getters (the content does not change midway).",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no outside boundary and no state. validate and checkSchema do not throw an exception."
        ],
        "do_not_use_when": [
          "You need the whole of JSON Schema 2020-12. Only the 28 fixed keywords can be used, and format is refused without being checked.",
          "You want to check the current actual contract schemas (the 17 from 00a) as they are. All 17 use things outside the subset (format, runtime_validator_contract, uniqueItems, hash_computation, external $refs), so each whole schema is refused.",
          "You need a $ref that uses an external document, a base URI from $id, $anchor or $dynamicRef. $ref is only \"#\" and \"#/$defs/<name>\".",
          "You use a pattern from an untrusted schema as it is. It does not stop regular-expression blowup (ReDoS), and the limit on the number of evaluations does not count the time spent inside a regular expression.",
          "You need a limit on the length of strings and arrays or on the size of const and enum comparisons. It has no limit; a limit is decided by the I-06 part or by the caller.",
          "You want integers of 2^53 or more to be treated as integer, or strings that look the same to be treated as the same. integer means safe integers only, and it does no Unicode normalization.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED; the Unicode version used by the RegExp u flag depends on the engine)."
        ],
        "what_it_checks": [
          "When a keyword outside the subset (including format) appears anywhere (even inside an unused $defs), or when $schema, $id or $defs appears somewhere other than the root, it stops the whole schema with UNSUPPORTED_KEYWORD.",
          "When $schema is not 2020-12 it stops with UNSUPPORTED_DIALECT, when a $ref has the wrong form (an external document, an anchor, or % or ~ notation) with UNSUPPORTED_REF, and when the name is not in $defs with UNRESOLVED_REF.",
          "When there is a $ref that loops without advancing through the instance, it stops with REF_CYCLE whether or not the loop is reached.",
          "When a keyword's value has the wrong shape (an empty enum, duplicate entries in required or type, a negative number, items in array form, then without if) it stops with INVALID_SCHEMA, and when a pattern cannot be compiled with INVALID_PATTERN.",
          "When the schema is not a JSON value (including getters) it stops with SCHEMA_NOT_JSON, and when the instance is not a JSON value (undefined, a function, NaN, a bigint, a Date, an array with holes, a symbol key, a getter and so on) with INSTANCE_NOT_JSON.",
          "When nesting exceeds 256 levels it stops with SCHEMA_TOO_DEEP or INSTANCE_TOO_DEEP, when the evaluation depth reaches 512 with EVALUATION_TOO_DEEP, and when the number of evaluations exceeds 1,000,000 with EVALUATION_LIMIT. Even when a limit is reached inside not, anyOf, oneOf or if, it does not turn into \"does not match\".",
          "When the instance does not match the schema, it stops with a fixed code for each keyword, such as TYPE_MISMATCH, REQUIRED_MISSING, ADDITIONAL_PROPERTY, PATTERN_MISMATCH or ONE_OF_MANY_MATCHED. Integers of 2^53 or more do not match integer.",
          "When inspecting a value itself raises an exception (a Proxy trap and so on), it returns INSPECTION_FAILED and does not throw. Even if a Proxy changes its answers, the result does not change midway, because the check is done on a copy read once."
        ],
        "what_it_does_not_do": [
          "It handles only a subset of JSON Schema, not the whole (the 28 fixed keywords). format is refused without being checked, and $refs to external documents, $anchor and $dynamicRef are refused too.",
          "The actual contract schemas (the 17 from 00a) are all refused as outside the subset. The subset has not been silently widened; whether to widen the subset or to bring the contracts into line with it is a separate decision.",
          "It has not been run against the official JSON-Schema-Test-Suite. The known answers are the ones written in this part's own tests.",
          "It does not stop regular-expression blowup (ReDoS) in pattern. Patterns from untrusted schemas are to be restricted by the caller.",
          "Matching the schema is a shape check. It does not mean that the value's content is correct, that it may be accepted, or that it is permitted (judging meaning is for the Kernel, and approval is for people; result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular JSON Schema library."
        ],
        "integration": "Call validate(schema, instance) in src/schema-validate.js (it does not throw). checkSchema(schema) checks only the schema (it does not throw), and assertValid throws SchemaValidateError when there is no match. Input: a schema that uses only the fixed keywords, and an instance that is a JSON value. Returns: { ok: true, errors: [] } or { ok: false, errors: [{ code, instance_path, schema_path }] } (the first error only; code is from error_taxonomy). It does not fetch external $refs. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-02",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Output encoding — Output encoding part (Primitive)",
        "summary": "The Output encoding part (Primitive) is a standalone part that turns a string into a form that is read as characters within one specified output destination (context). There are only 4 contexts: html_text, html_attribute_quoted, url_component and json_in_html_script. A context that is not defined, a value that is not a string, a lone surrogate or input that is too long is stopped with a fixed code (fail-closed). It is not HTML sanitization.",
        "problem": "When a string is written out into HTML, a URL or a <script>, it prevents encoding by guesswork without deciding the context, and silently replacing a broken string (a lone surrogate). The context must be stated explicitly by name, and there is no default context.",
        "use_when": [
          "You want to put a string into the content (text) of an HTML element (html_text; it turns & < > \" ' into entity references).",
          "You want to put a string into an HTML attribute value enclosed in double quotes (html_attribute_quoted; in addition to the 5 characters of html_text, it also encodes `).",
          "You want to put a string into one component of a URL (one path segment, or a query name or value) (url_component; everything other than RFC 3986 unreserved characters becomes %XX).",
          "You want to place a string as a JSON string literal inside an HTML <script> element (json_in_html_script; < > & U+2028 U+2029 are written in \\u form, so </script> or <!-- cannot close the element).",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no outside boundary and no state. encode does not throw an exception, and reversing the encoding gives back the original text."
        ],
        "do_not_use_when": [
          "You want to sanitize HTML input while keeping tags and attributes. This part is not HTML sanitization; it writes everything as characters.",
          "You want to neutralize the meaning of values in unquoted attributes, event attributes (onclick and so on), style attributes or attributes that take URLs (javascript: in href or src and so on). html_attribute_quoted does not prevent that.",
          "You need a check of a whole URL (allowing schemes, checking hosts). url_component is for one component of a URL.",
          "You write into an HTML attribute, into a JavaScript string or template literal, or into a raw-text element such as style. json_in_html_script is for the inside of a <script> element.",
          "You write into contexts such as CSS, JavaScript identifiers, XML, SQL, shell or headers. These are not handled.",
          "You handle strings longer than 1,048,576 UTF-16 code units. It stops them with INPUT_TOO_LARGE.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When context is not one of the 4 defined ones (including differences in case or whitespace, Object.prototype names and values that are not strings), it stops with UNKNOWN_CONTEXT.",
          "When text is not a primitive string (a number, null, undefined, a String object, an array, a Symbol, a bigint, a Proxy), it stops with NOT_A_STRING.",
          "When text is longer than 1,048,576 UTF-16 code units, it stops with INPUT_TOO_LARGE.",
          "When text contains a lone surrogate (an unpaired U+D800 to U+DFFF, including a reversed pair or a lone one at the end), it stops with INVALID_UNICODE in every context. It does not replace it with U+FFFD.",
          "The order of stopping is context → whether it is a string → length → lone surrogate, and only the first one hit is returned."
        ],
        "what_it_does_not_do": [
          "The item's name refers to \"injection prevention\", but this part is only encoding that writes characters as characters within one specified context; it does not include HTML sanitization (processing that keeps allowed tags).",
          "It does not decide which value goes into which context (assembling templates). That stays with the caller or with another part.",
          "Encoding for one context does not make the string safe in another context.",
          "How browsers actually parse the output has not been checked on real browsers. The rules were derived from the HTML, URL and JSON specifications and checked with Node.js v24.21.0 built-in functions and an independent oracle.",
          "It does no Unicode normalization. Even if two strings look the same, different code units give different output.",
          "Being able to encode a string does not mean that it may be put into another context, that it has been made harmless as HTML, or that its content may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular template engine or escaping library."
        ],
        "integration": "Call encode(text, context) in src/encode.js (it does not throw). Input: a primitive string text (up to 1,048,576 UTF-16 code units) and a context that is one of html_text, html_attribute_quoted, url_component or json_in_html_script. Returns: { ok: true, errors: [], text } (the encoded string) or { ok: false, errors: [{ code }] } (code is from error_taxonomy; there is no text). Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-03",
        "category": "I/検証・無害化・安全",
        "kind": "Tsugite",
        "title": "Malware scanning — Malware Scan Receipt Tsugite",
        "summary": "The Malware Scan Receipt Tsugite (継手, tsugite) submits uploaded attachments and similar files to a pinned scanner, and checks the returned scan receipt (ScanReceipt) against the target digest, the pinned engine and version, the scan time window, the vocabulary and the contract's const. The scan itself is done by an external engine, and this Tsugite does not include a scan engine. Whatever the result, it keeps quarantine and never says the file is safe (NOT_DETECTED ≠ SAFE; fail-closed).",
        "problem": "In upload quarantine, it prevents spoofed, stale or swapped scan results from passing, and prevents mistaking \"not detected\" for \"safe\" and lifting quarantine. Even with no detection it derives no safety (safety: NOT_DERIVED_FROM_SCAN), and lifting quarantine is left to a Policy or human decision.",
        "use_when": [
          "You want an external scan engine to scan uploaded attachments and similar files, and want to check the result before handling them.",
          "You want to match scan receipts against the target digest, the pinned scanner, engine and version, and the time window since submission, so that spoofed, stale or swapped results do not pass.",
          "You want to keep quarantine regardless of the scan result and leave lifting it to a Policy or human decision.",
          "You do not want scan results with no verdict or an unknown verdict to be treated as clean."
        ],
        "do_not_use_when": [
          "You want the malware-scanning engine itself. This Tsugite does not include a scan engine. Scanning is done by an external engine, and which engine to connect to has not been decided.",
          "You want to use \"not detected\" as \"safe\". Even for NOT_DETECTED it does not call the file safe (safety: NOT_DERIVED_FROM_SCAN) and keeps quarantine.",
          "You want this Tsugite to lift quarantine. Lifting it is a Policy or human decision, and this Tsugite does not handle it.",
          "You need moderation (I-04). That is a separate part.",
          "You want to treat scan results as grounds for approval, trust or authorization. No result is approval, and no trust or authorization is derived (NOT_DERIVED).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the submission. Content types that are not allowed, files over the size limit, and tampered content (bytes that do not match the digest) are not submitted (REFUSED).",
          "It checks the scanner and engine. A scanner that is not pinned is not accepted, and a receipt from an engine or version not in the pinned list is not accepted. If a receipt comes from an engine that is not pinned, it stops from then on (HALTED).",
          "It matches the receipt's target. A receipt with a spoofed request_digest, or one with a swapped target digest, is not accepted, and it stops from then on (HALTED).",
          "It checks the time window. A receipt dated before the submission, or one arriving after max_scan_seconds from submission, is treated as stale and not accepted.",
          "It checks vocabulary and evidence. A receipt with an unknown verdict, DETECTED without evidence, or a claim of safe, trusted or authorized is not accepted.",
          "INCONCLUSIVE and UNKNOWN verdicts become SCAN_INCONCLUSIVE and are not treated as clean.",
          "A scanner failure is treated as UNKNOWN. The same submission is not sent twice, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not scan for malware. It includes no scan engine; the scan itself is done by an external engine. The actual scan engine is undecided, and the port implementation and what to pin (engine, version, receipt format) will be settled at a separate gate when connecting.",
          "It does not organize the case after a detection. That is the role of the Kernel case-support. Lifting quarantine is a Policy or human decision.",
          "It does not derive safety. Every result carries safety: NOT_DERIVED_FROM_SCAN, quarantine: KEEP_UNTIL_POLICY_DECISION and approval: false.",
          "It does not lift quarantine. Lifting it is a Policy or human decision.",
          "It does not handle moderation (I-04). That is a separate part.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use antivirus software."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1. Input: pass a ScanSubmitRequest and the bytes to scan(ScanSubmitRequest, bytes) on createScanClient({ cfg, port }). Returns: one of DETECTED_QUARANTINE_KEPT, NOT_DETECTED_QUARANTINE_KEPT, SCAN_INCONCLUSIVE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the external scan engine (submit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE (an external submission).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-04",
        "category": "I/検証・無害化・安全",
        "kind": "Tsugite",
        "title": "Content moderation — Content Moderation Tsugite",
        "summary": "The Content Moderation Tsugite (継手, tsugite) checks a request (ModerationRequest) asking an external moderation provider or AI to observe one piece of content (a post, an image and so on), carries it, and matches the returned receipt (ModerationReceipt) against the contract. The labels it returns are observations only; it does not derive removal, stopping, publication, a human judgement or approval. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "Handing content to an external provider is an irreversible disclosure (EXTERNAL_IRREVERSIBLE). It prevents handing over bytes that have not been checked, handing content to a provider or content type that the disclosure basis does not cover, and mistaking a returned label for removal, a refusal to publish or a human judgement.",
        "use_when": [
          "You want an external moderation provider or AI to look at content such as a post or an image, and to receive its observation (labels) as material for a decision.",
          "You want to pin the provider, the policy version and the model version in the configuration, and not have content observed with versions outside what is pinned. Receipts with a mismatched version are not accepted either.",
          "You want content to be handed out only when the disclosure basis (disclosure_basis_ref) covers that provider and content_type.",
          "You want to confirm that the bytes of the content being sent are the same as the content_digest stated in the request before handing them over."
        ],
        "do_not_use_when": [
          "You want removal, unpublishing or other enforcement actions to be taken automatically from the observation result. The result of this Tsugite is an observation of facts, not an enforcement action (enforcement_action is NOT_DERIVED).",
          "You want to run a model locally to observe content. This Tsugite asks an external provider or AI; local observation is within the scope of a separate Primitive.",
          "The content must not be handed outside. Sending it is an irreversible disclosure (EXTERNAL_IRREVERSIBLE), and even for a request whose result is unknown (UNKNOWN), the disclosure may already have happened.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a connection to a provider that is ready to use. It has never connected to an actual provider or AI; the port implementation, credentials and configuration values will be settled at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks what the configuration pins. If there is no configuration, a list is empty or a table is missing, or the provider, policy, policy version, model or model version is outside what is pinned, or the model belongs to another provider, it does not send.",
          "It checks the disclosure basis. With a basis that is not in the configuration, a basis that does not cover that provider or content_type, or a basis that is not in Ref form, it does not send.",
          "It checks the content bytes. If there are no bytes, their sha256 differs from content_digest, or they were changed after being checked, it does not send. A request that carries the content itself is also refused.",
          "It checks the form of references and of the request. References that are empty, are absolute drive paths, contain .., start with /, or contain control characters or U+2028/2029, a request_id that is not a Ref, a content_type that is not a media type, and additional fields are not sent. Malformed references are not let through even if the configuration pins them.",
          "It matches the receipt. A spoofed request_digest, a receipt for a different request or a different provider, a copied content_digest that differs, a receipt that is not CONTENT_MISMATCH even though the digest of the received content differs, and a mismatched policy or model version are not accepted.",
          "It checks the form of labels. A verdict_state or level outside the vocabulary, OBSERVED without labels, a receipt that has labels but is not OBSERVED, overlapping categories, and a score_bp that is not an integer from 0 to 10000 are not accepted.",
          "Receipts that derive an enforcement action, a human judgement or a publication decision (where enforcement_action, human_judgement or publication_decision is not NOT_DERIVED), and receipts with an effect the state does not allow or an UNKNOWN effect, are not accepted, and it stops (HALTED).",
          "A request whose result is unknown is resolved by resending the same request (lookup), and no other requests are sent until it is resolved. A different request with the same request_id, swapping in a different request for an unresolved one, and a request changed while being sent are not sent."
        ],
        "what_it_does_not_do": [
          "It takes no moderation action. This Tsugite carries requests for observation; it does not include removal, unpublishing or judging violations. The result is an observation of facts, and every result carries NOT_DERIVED for enforcement_action, human_judgement and publication_decision, and approval: false.",
          "It does not check whether labels are correct, whether they fit the policy, or what score_bp means. The content of the observation is the provider's answer. Judging something a violation is the role of a separate human judgement and of Kernels.",
          "It does not create the disclosure basis (records of consent or approval), and does not check whether it is valid, within its term or not withdrawn. It only looks at whether it is in the configuration. The basis is the role of people and organizations outside the part (APPROVAL_OUTSIDE_PART).",
          "The bytes the provider actually received can be confirmed only when the provider returns received_content_digest. If it does not, confirmation extends only to handing the checked bytes to the port.",
          "It checks only the form of the time (observed_timestamp), not whether a receipt is old or reused. The matching records and unresolved requests exist only inside this Tsugite and are lost on restart.",
          "It holds no credentials for authenticating with the provider; the port holds them (HELD_BY_PORT). It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific moderation API."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/content-moderation/v1. Input: pass a ModerationRequest and the content bytes to submit(request, bytes) on createModerationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries verdict_state, labels and other fields in its result. Every result has approval: false. cfg needs four pinned lists (allowed_providers and others). User provides: a port to the provider (submit, and optionally lookup) and credentials. Side effect: EXTERNAL_IRREVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-05",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "File type validation — File type validation part (Primitive)",
        "summary": "The File type validation part (Primitive) is a standalone part that identifies the type and MIME from the magic bytes at the start of bytes by comparing them with a versioned table (7 types: pdf, png, jpeg, gif, zip, gzip, webp), and compares the result with the declared MIME and the list of allowed MIMEs. It accepts bytes only. An unidentifiable type, a mismatch with the declaration or a type that is not allowed is stopped with a fixed code (fail-closed).",
        "problem": "It prevents accepting a file whose real content is different because a disguised extension or a declared Content-Type was trusted. It compares the type identified from the bytes with the declaration, makes the allowed list mandatory, and also stops the configuration mistake of writing a MIME that is not in the table into that list.",
        "use_when": [
          "You want to identify the type and MIME of a received file from the magic bytes at the start of its bytes (detect).",
          "You want to accept a file only when the declared MIME (declared_mime) exactly matches the real content of the bytes and is in the list of allowed MIMEs (allowed_mimes) (check).",
          "You want the check to take the same fixed effort even for large files. It reads at most the first 12 bytes.",
          "You need a way of calling it that does not throw. check returns { ok, errors, detected }, does not throw an exception, and does not call getters on the options.",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no outside boundary and no state."
        ],
        "do_not_use_when": [
          "You want to check whether the content is harmful (malware, JavaScript in a malicious PDF, a zip bomb and so on). That is the scope of I-03.",
          "You want to confirm that the rest of the file has the correct form (that it is not broken, that it can be opened). It identifies only the magic bytes at the start.",
          "You want to identify the inner format of formats built on zip, such as docx or xlsx. It identifies them only as application/zip, and declaring the MIME of the inner format results in TYPE_MISMATCH.",
          "You want to handle types other than the 7 in the table (text, json, svg, mp4, webm, tiff, bmp, heic, 7z, rar and so on). It stops them with UNKNOWN_TYPE.",
          "You want this part to normalize uppercase letters, parameters (; charset= and so on) or aliases (image/jpg and so on) in the declared MIME. It does no such normalization, so the caller removes them and lowercases the MIME before passing it.",
          "You need a size limit on the bytes. It has no limit; a size limit is decided by the I-06 part or by the caller.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the input is not bytes (Uint8Array), such as a string, an array of numbers, an ArrayBuffer or another typed array, check stops with NOT_BYTES (detect throws FileTypeError).",
          "When the bytes match the magic bytes of no type in the table (a truncated header, a 1-byte difference, GIF88a, lowercase %pdf-, an empty zip archive or a split-archive marker, another gzip method, RIFF WAVE, a shifted position), it stops with UNKNOWN_TYPE.",
          "When the identified MIME does not exactly match declared_mime (an alias, a parameter, uppercase, an Office MIME declared for a zip), it stops with TYPE_MISMATCH.",
          "When the identified MIME is not in allowed_mimes, it stops with NOT_ALLOWED.",
          "When allowed_mimes is missing, empty, has holes, is an array-like object, or contains a MIME that is not in the table, it stops with INVALID_OPTIONS.",
          "When declared_mime is not a string, when opts has an unknown key (including a symbol) or a getter, or when opts is a Proxy trap or a class instance, it stops with INVALID_OPTIONS and does not throw.",
          "When it stops, there is always exactly one error, and the order of checks is bytes, options, whether the type can be identified, the declaration, and the allowed list."
        ],
        "what_it_does_not_do": [
          "Identifying the type is not a check of whether the content is safe. Malware scanning (I-03), size limits (I-06) and checks of the inner structure (such as decoding images) are separate parts, and combining them is up to the caller.",
          "Matching magic bytes is not proof that the rest of the file has the correct form. It does not read the PDF xref, PNG chunks, the zip central directory and so on.",
          "The magic-bytes copies inside the Tsugite (2 content.js files) have not yet been replaced with this part. The pdf, png and jpeg checks were compared once in the workspace and showed no difference, but the copies differ in that they also handle text/plain and application/json and accept only a Buffer, and any replacement is separate work.",
          "It does not identify the inner format of formats built on zip, or empty or split zips.",
          "Identifying the type does not mean that the file may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a copy of, or a template for how to use, a particular file type detection library (file-type, libmagic and so on)."
        ],
        "integration": "Call check(bytes, opts) in src/filetype.js (it does not throw). Input: bytes (a Uint8Array, including a Buffer) and a plain object opts with only allowed_mimes (required) and declared_mime (optional). Returns: { ok: true, errors: [], detected: { type, mime } } or { ok: false, errors: [{ code }], detected } (code is from error_taxonomy). detect(bytes) returns { type, mime } or null (it throws if given something that is not bytes). Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-06",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Size and quantity limits — Size and quantity limit part (Primitive)",
        "summary": "The Size and quantity limit part (Primitive) is a standalone part that counts the size of a value in one of 6 fixed units (bytes, utf8_bytes, utf16_units, code_points, items, keys) and compares it with an upper and a lower limit. Each unit accepts a fixed type of value, and a value that does not fit is stopped. The upper limit is required; exceeding it stops with TOO_LARGE, and falling short stops with TOO_SMALL (fail-closed). The caller decides the unit and passes it in.",
        "problem": "In the upper-limit check that refuses oversized input, it prevents counting a value that cannot be counted as 0, counting a string as bytes or an array-like object as an array, silently counting the UTF-8 length of a string that contains a lone surrogate, and accepting a range with no upper limit or an empty range.",
        "use_when": [
          "You want to apply a unit and an upper limit decided by the caller to the size of a body, a name, an array or an object (for example, 1 MiB in utf8_bytes for a body and 64 in code_points for a name).",
          "You want to handle, by stating the unit explicitly, the fact that the same string counts differently depending on the unit (one emoji is 4 in utf8_bytes, 2 in utf16_units and 1 in code_points).",
          "You want to decide where the boundaries of the upper and lower limits fall. By default the upper limit is inclusive (max_inclusive), and the lower limit defaults to 0 and is always inclusive.",
          "You want to apply a size limit before passing a value to the I-08 canonicalization, B-07 hashing or I-05 file type parts. Those parts have no size limit.",
          "You need a way of calling it that does not throw. check does not throw an exception, does not modify the value or the options, and does not call getters.",
          "You want to call it locally, as processing that does not depend on the clock, random numbers or the environment's locale and that has no state."
        ],
        "do_not_use_when": [
          "You want to limit by the number of visible characters (grapheme clusters, such as 👨‍👩‍👧 or 🇯🇵). It does not count grapheme clusters.",
          "You want strings that look the same to have the same length. It does no Unicode normalization, so é and e plus a combining character count differently.",
          "You want to limit by size including nested contents (arrays inside arrays, keys inside objects). items and keys count only the top level.",
          "You want to stop oversized input before the value is read into memory. This part counts values that are already in memory.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the size exceeds the upper limit (or equals it, if max_inclusive is false) it stops with TOO_LARGE, and when it is below the lower limit with TOO_SMALL, and it returns the counted size (size).",
          "When the type of the value does not fit the unit (for bytes, a string, an ArrayBuffer or another typed array; for string units, bytes or a String object; for items, an array-like object or a Set; for keys, an array, a Map, a Date, a class instance and so on), it stops with VALUE_TYPE_MISMATCH.",
          "For utf8_bytes, when the string contains a lone surrogate (an unpaired U+D800 to U+DFFF), it stops with INVALID_UNICODE.",
          "When unit is none of the 6 units (including uppercase and aliases), it stops with UNKNOWN_UNIT.",
          "When max is missing, when max or min is not a safe integer of 0 or more, when min > max, or when the range admits no size at all, it stops with INVALID_LIMITS.",
          "When opts is not a plain object, has an unknown key or a getter, or max_inclusive is not a boolean, it stops with INVALID_OPTIONS, and when inspecting the value itself raises an exception (a Proxy trap and so on) with INSPECTION_FAILED."
        ],
        "what_it_does_not_do": [
          "Of the \"blocking oversized input\" in its name, it cannot stop the reading of an oversized value itself. The work of loading a value into memory before passing it is outside this part.",
          "It does not decide which unit to use or what the limit is. The caller decides (the policy of each Tsugite, contract or Kernel).",
          "It does not handle grapheme counts, Unicode normalization or the size of nested contents.",
          "The size-limit checks inside the Tsugite and the I-06 records in the workshop ledger have not yet been replaced with this part. Any replacement is separate work. For a string with a lone surrogate, utf8_bytes differs from Buffer.byteLength and stops.",
          "Proxy traps were checked with only a few unit tests (they are not included in the fuzzing).",
          "Being within the range does not mean that the value's content is correct or that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular validation library."
        ],
        "integration": "Call check(value, opts) in src/size.js (it does not throw). Input: a value and a plain object opts with only { unit, max, min?, max_inclusive? }, where unit is one of the 6 units and max is a required safe integer of 0 or more. Returns: { ok: true, errors: [], size } or { ok: false, errors: [{ code }], size } (code is from error_taxonomy; size is a number only for TOO_LARGE and TOO_SMALL, and null otherwise). measure(value, unit) returns an integer of 0 or more and throws SizeError on a value it cannot accept. Side effect: NONE. There is no state, no outside connection, no credential and nothing for the caller to provide, and the only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-07",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Deduplication — Deduplication part (Primitive)",
        "summary": "The Deduplication part (Primitive) is a standalone part that identifies duplicates of the same key within a given list (an array of { key }) and a given index (known_keys). It puts the positions that appear for the first time into kept, and the second and later occurrences and those found in the index into duplicates ({ index, of }). Keys are compared as UTF-16 code units, unchanged. Input it cannot accept is stopped with a fixed code and position (fail-closed).",
        "problem": "In identifying duplicates, the counterpart of reconcile, it prevents silently skipping entries of the wrong shape, mistaking keys with names such as __proto__ or constructor, and accepting input with no limit on the number of entries or the length of keys. The same input always gives the same answer.",
        "use_when": [
          "You want to identify duplicates of the same key within the list passed in a single call. Each position goes into exactly one of kept and duplicates, once.",
          "You want to pass keys seen before as known_keys and also identify duplicates against them, as of: \"known\".",
          "You want to compare keys made with the I-08 canonicalization or B-07 hashing parts by exact code-unit match. Which values the key is made from is decided by the caller.",
          "You want input that cannot be accepted to be stopped with a code and position (JSON Pointer) instead of being silently skipped.",
          "You want to call it locally, as processing that does not throw an exception, does not modify its input, and has no state."
        ],
        "do_not_use_when": [
          "You want to find duplicates against a stored index or across other calls. It compares only within its arguments, and it neither reads nor writes a persistent or global index (that is on the storage Tsugite's side).",
          "You want keys that look the same (é and e plus a combining character, A and a, differences in leading or trailing whitespace) to be treated as the same. It does no normalization, case folding or whitespace trimming. If you want them treated as the same, the caller makes the key by fixed rules before passing it.",
          "You want to handle a list of more than 100,000 entries in one call. The caller splits it. Duplicates across split calls are not identified unless the earlier keys are passed as known_keys.",
          "You want duplicates to be judged by the content of an entry (properties other than key). Duplicates are judged by key only.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When entries is not a plain array (including subclasses of Array) it stops with INVALID_ENTRIES, and when it has more than 100,000 entries with TOO_MANY_ENTRIES.",
          "When an element is not a plain object (including holes, getters, class instances and a null prototype), or does not have an own data property key (a getter or an inherited one), it stops with MALFORMED_ENTRY.",
          "When key is not a string or is empty it stops with INVALID_KEY, and when it exceeds 4096 UTF-16 code units with KEY_TOO_LONG.",
          "When known_keys is not a plain array it stops with INVALID_KNOWN_KEYS, when it has more than 100,000 entries with TOO_MANY_KNOWN_KEYS, and when the same key appears twice with DUPLICATE_KNOWN_KEY (at the position of the second one).",
          "When options is not a plain object, has a name other than known_keys, or known_keys is a getter, it stops with INVALID_OPTIONS, and when inspecting a value itself raises an exception (a Proxy trap and so on) with INSPECTION_FAILED.",
          "When it stops, it returns the code and position (path, a JSON Pointer) of the first problem found, and returns neither kept nor duplicates."
        ],
        "what_it_does_not_do": [
          "Of the \"deduplication\" in its name, it does not delete duplicates from stored data. It only sorts the positions of the list in its arguments into kept and duplicates. Looking up and writing a stored index is on the storage Tsugite's side.",
          "Of the \"detection of identical items\" in its name, it does not look at whether the contents are the same. It uses only exact code-unit matching of keys, with no normalization or case folding.",
          "It cannot tell duplicates against an index that was not passed in (other calls, stored data).",
          "Accepting exactly the limit of 100,000 entries was checked only by unit tests (the fuzzing covers only inputs over the limit).",
          "Not being a duplicate does not mean that the content is correct or that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular deduplication library."
        ],
        "integration": "Call dedupe(entries, options) in src/dedupe.js (it does not throw). Input: a plain array of plain objects { key } (up to 100,000 entries) and options that are omitted or contain only { known_keys }. Returns: { ok: true, errors: [], kept, duplicates } (kept in ascending order of position; duplicates as { index, of }, where of is a position or \"known\") or { ok: false, errors: [{ code, path }] } (code is from error_taxonomy, path is a JSON Pointer). Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-08",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Canonicalization — Canonicalization part (Primitive)",
        "summary": "The Canonicalization part (Primitive) is a standalone part that turns a JSON value into a canonical string (UTF-8 bytes) under the RFC8785-restricted rules. It orders keys by UTF-16 code units, inserts no whitespace, and accepts only safe integers as numbers. A value it cannot accept is stopped with a fixed code and position (fail-closed).",
        "problem": "In the preprocessing for signing and reconciliation, it prevents the bytes that a digest is computed from from changing because of variation in key order, whitespace or the way numbers are written, or because a value that cannot be accepted is silently skipped. The same value always produces the same string.",
        "use_when": [
          "You want to produce, from a JSON value, the canonical bytes that a digest is computed from, as preprocessing for signing or reconciliation (canonicalBytes).",
          "You want a value that cannot be accepted (a fraction, undefined, a function, an object that is not plain, an array with holes, and so on) to be stopped with a code and a position (JSON Pointer) instead of being silently skipped.",
          "You need a way of calling it that does not throw. tryCanonicalize returns { ok, errors, text } and does not throw an exception.",
          "You need deterministic output that does not depend on the clock, random numbers or the environment's locale.",
          "You want to call it locally, as processing with no outside boundary and no state. It has no dependency packages."
        ],
        "do_not_use_when": [
          "You want to canonicalize a value that contains fractional or exponent numbers. Numbers are safe integers only (-(2^53-1) to 2^53-1); this is not the whole of RFC 8785.",
          "You want strings that look the same to be treated as the same. It does no Unicode normalization, so é and e plus a combining character are different strings.",
          "You are looking for broad unification like the catalog's one-liner \"unifying variations in representation (RFC8785 and similar)\". What this part does is limited to the profile of RFC 8785 (JCS) that restricts numbers to safe integers.",
          "You handle values nested more than 1000 levels deep. It stops them with TOO_DEEP.",
          "You need a size limit on values. It has no limit on the length of strings, arrays or keys; a size limit is decided by the I-06 part or by the caller.",
          "You use it on a version or engine other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "For a fraction, an integer outside the safe range, NaN or Infinity, it stops with NOT_A_SAFE_INTEGER.",
          "For undefined (as the value itself, an array element or an object member) it stops with UNDEFINED_VALUE, and for a function, a symbol or a bigint with UNSUPPORTED_TYPE.",
          "For a subclass of Array, an array with holes, or an array with properties other than its elements, it stops with NOT_A_PLAIN_ARRAY, SPARSE_ARRAY or EXTRA_ARRAY_PROPERTY.",
          "For an object that is not plain (Date, Map, Buffer, a null prototype, a class instance and so on), a symbol key, or an object with a non-enumerable property, it stops with NOT_A_PLAIN_OBJECT, SYMBOL_KEY or NON_ENUMERABLE_PROPERTY.",
          "When there is a getter or setter property, it stops with ACCESSOR_PROPERTY without calling it. The content does not change while the value is being inspected.",
          "When nesting exceeds 1000 levels, it stops with TOO_DEEP without overflowing the stack.",
          "When inspecting the value itself raises an exception (a Proxy trap and so on), tryCanonicalize returns INSPECTION_FAILED and does not throw.",
          "When it stops, it returns the code (code) and the position where it stopped (path, a JSON Pointer per RFC 6901; the root is the empty string)."
        ],
        "what_it_does_not_do": [
          "It does not handle the whole of RFC 8785 (the way fractional and exponent numbers are written). Numbers are safe integers only.",
          "It does no Unicode normalization. Even if two strings look the same, they are different strings if their code units differ.",
          "It does not decide which fields go into a digest or which values are excluded. That stays with the caller (each Tsugite or contract).",
          "The canonicalization copies kept inside the Tsugite (20 of them) have not yet been replaced with this part; they still have their own copies. For values the copies handle correctly, the output is the same byte for byte (or both stop the same way), but for values where a copy produces broken JSON or incomplete output (an array with holes, a symbol key and so on), this part stops. The copies' handling is unchanged, and any replacement is separate work, done after the compatibility table has been checked.",
          "Producing a canonical string does not mean that the value's content is correct, nor that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "It has not checked how JSON.stringify writes strings on engines other than Node.js v24.21.0.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular JSON library."
        ],
        "integration": "Call tryCanonicalize(value) in src/canonical.js (it does not throw). canonicalize and canonicalBytes return the canonical string and a UTF-8 Buffer, and throw CanonicalizationError on a value they cannot accept. Input: one JavaScript value (a nesting of null, booleans, strings, safe integers, dense arrays and plain objects). Returns: { ok: true, errors: [], text } or { ok: false, errors: [{ code, path }] } (code is from error_taxonomy; path is a JSON Pointer). Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing. The only dependencies are Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-09",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Readability check — Readability check (gzip, JSON, TSV, shallow PDF/image check) part (Primitive)",
        "summary": "The Readability check part (Primitive) is a standalone part that checks whether fetched bytes can be opened (gzip decompression, UTF-8, JSON parsing, the number of TSV columns, the markers of PDF, PNG and JPEG) and returns the result as facts. Being unreadable is not an error; it is returned as the facts readable: false and reason. It does not make the decision to stop what cannot be opened (it is not a gatekeeper).",
        "problem": "You do not notice that a file is unreadable until you open it. This part returns a broken gzip, invalid UTF-8 or broken JSON as facts without throwing, and stops the decompressed size at 1 GiB so that a gzip bomb cannot use memory without limit. Because it does not infer format from the content, it does not reinterpret content that differs from its declaration as another type.",
        "use_when": [
          "You want to record as a fact whether fetched bytes can be opened as their declared type (probeReadability).",
          "You want gzip-compressed bytes to be decompressed and then checked (compression: \"gzip\"; if it cannot decompress, GUNZIP_FAILED).",
          "You want to know, as facts, the top-level type of JSON (top_type) or the number of rows and columns of TSV (rows, columns).",
          "You want a shallow check of whether PDF, PNG or JPEG has its markers (for PNG, also the IHDR width, height, bit_depth and color_type).",
          "You want unreadability not treated as an error, and want to receive why it could not be read (reason, and the line number and so on for TSV_RAGGED).",
          "You want to call it locally as deterministic processing that has no clock, no random numbers and no outside connection. It does not open files; the caller passes the bytes."
        ],
        "do_not_use_when": [
          "You want to confirm that a PDF or image can be rendered, that the page or image data is not corrupted, or that it is not encrypted. readable: true only means the markers are present (markers_only: true).",
          "You want to read character encodings other than UTF-8 (Shift_JIS and so on). If charset is not utf-8, the result is CHARSET_NOT_SUPPORTED, with no conversion and no guessing. Shift_JIS with no declared charset becomes NOT_UTF8.",
          "You want the type guessed from the first bytes of the content. format is decided only from the type/subtype of the declared media type.",
          "You want TSV quoting (\\t or line breaks inside \"…\") to be handled. \\t is always counted as a delimiter.",
          "You want files with bytes before the PDF header, more than 1024 bytes after %%EOF, or bytes after the JPEG EOI, to be treated as openable. They result in the fact that the markers are missing.",
          "You want to check input larger than 256 MiB, or input larger than 1 GiB after decompression. It stops with INPUT_TOO_LARGE (with limit). This is not the fact that it is unreadable; it means it was not checked.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with NOT_BYTES when the input is not an object, when bytes is not a Uint8Array (a string, an array of numbers, an ArrayBuffer, another typed array), or when reading a property itself raises an exception.",
          "It stops with INVALID_MEDIA_TYPE when declared_media_type is not a string of the form in the contract raw-fetch/v1 or is longer than 255 characters, and with INVALID_COMPRESSION when compression is neither \"gzip\" nor \"none\" (including when it is omitted).",
          "Before reading the bytes, it returns MEDIA_TYPE_PARAMETERS_UNREADABLE when the parameters of the declaration cannot be read as RFC 9110 parameters or charset appears two or more times, and CHARSET_NOT_SUPPORTED when charset is not utf-8 for json, tsv or text.",
          "It decodes UTF-8 in fatal mode; overlong sequences, surrogates, values above U+10FFFF and truncated sequences are NOT_UTF8. It does not strip a leading BOM; JSON with a BOM is JSON_PARSE_FAILED.",
          "A TSV whose non-empty lines do not all have the same number of columns is TSV_RAGGED, and it returns in facts the line number and column count of the first line that did not match.",
          "For PDF it checks the leading %PDF- and %%EOF within the last 1024 bytes; for PNG, the 8-byte signature, an IHDR of length 13 and its CRC-32; for JPEG, the leading FFD8 (SOI) and trailing FFD9 (EOI). If they are missing, the result is PDF_MARKERS_MISSING, PNG_SIGNATURE_OR_IHDR_BAD or JPEG_MARKERS_MISSING.",
          "When the input is larger than 256 MiB, the decompressed output is larger than 1 GiB, or the JSON text is larger than 256 MiB, it does not check and returns INPUT_TOO_LARGE with limit (input, gunzip_output, json_text).",
          "When the declared media type is not in the supported table, the result is FORMAT_UNKNOWN. It reads only the range of the view of the bytes it is given."
        ],
        "what_it_does_not_do": [
          "readable is only the fact of whether it can be opened; it does not mean that the content is correct, complete, safe, or may be accepted. For pdf, png and jpeg it means only that the markers are present.",
          "It does not make the decision to stop what cannot be opened (it is not a gatekeeper). Whether to stop is decided by the caller.",
          "It does not open, fetch or store files.",
          "gzip decompression is left to zlib, and the fuzz oracle also uses zlib. The correctness of zlib itself is outside the scope of this part's tests.",
          "Its behavior in an environment with insufficient memory has not been checked. If JSON.parse fails for a reason other than syntax, the result is also JSON_PARSE_FAILED.",
          "Readability results are not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular file format library."
        ],
        "integration": "Call probeReadability({ bytes, declared_media_type, compression }) in src/decode-probe.js (it does not throw). bytes is a Uint8Array (up to 256 MiB), declared_media_type is the declaration of the type of the content after decompression (the form in the contract raw-fetch/v1, up to 255 characters), and compression is \"gzip\" or \"none\". It returns { ok: true, errors: [], readable, format, facts, reason } or { ok: false, errors: [{ code }] }. There are no side effects, no state, no outside connection and no credentials, and there is no port for the caller to provide. Its only dependency is the Node.js built-in zlib.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-10",
        "category": "I/検証・無害化・安全",
        "kind": "Component",
        "title": "Fetched data probe (versioned) — Fetched data probe (3 stages, 4 states, versioned) part (Component)",
        "summary": "The Fetched data probe part (Component) is a part that, right after a fetch, checks the raw data in 3 stages (integrity, readability, usability) and keeps one of 4 states (done, need_input, failed, need_consent) in a separate record that carries the probe's version. It bundles the record check (the N-09 part (Primitive)), checked reading (the C-01 Tsugite), the readability check (the I-09 part (Primitive)), per-source extractors and idempotent writing (the C-05 Tsugite). It only reads the raw data; it does not delete or block it. done means the 3 items could be extracted, not that they are correct.",
        "problem": "Whether an import succeeded cannot be looked back on later. This part keeps the result of the probe in a separate record that carries the probe's version. It also prevents the probe from blocking the raw data, writing a false done when a port has failed, reading a fetch made by a Tsugite that is not in the list of admitted Tsugite, and moving the extracted values (which may include PII) into the result.",
        "use_when": [
          "Right after a fetch, you want to pass the fetch.json record (fetch_record) and the folder (folder_ref = raw/<source>/<YYYYMMDDTHHMMSSZ>) and write the probe result once to probe/<source>/<folder_stamp>/<probe_version>.json (probeFetch).",
          "You want to record, without reading the raw data, failed when the fetch failed, there is no record, or N-09 does not accept the record, and need_consent when tsugite_id does not exactly match an entry in the list of admitted Tsugite (certified_tsugite).",
          "You want to check integrity with the expected sha256 and size, readability with I-09, and usability by whether the per-source extractor could extract the 3 items (transaction date occurred_on, amount, counterparty), and learn which stage fell short as need_input with a reason.",
          "You want to apply a new version of the probe to past fetches again and create new records while keeping the old ones. The result key includes probe_version.",
          "From the pile of need_input results, you want to find which Tsugite and probe to build next (a source without an extractor gives need_input / NO_EXTRACTOR).",
          "You want to resend a write whose result is unknown as the same request (resolvePending)."
        ],
        "do_not_use_when": [
          "You want to use done as proof that the extracted values are correct or well-formed (how dates and amounts are written). done only means the 3 items could be extracted.",
          "You want to use the probe as a gatekeeper that blocks or deletes raw data that does not fit. This part does not write, delete or block raw data. The result is a separate record.",
          "You want it to also check that a file renders or is not broken. Readability is the shallow check of I-09 (for pdf, png and jpeg, whether the signature is present), and files beyond the I-09 limits are not checked (READABILITY_NOT_CHECKED).",
          "You want it to also take care of whether the extractor reads correctly or whether the list of admitted Tsugite is correct. Both are outside this part; it only looks for an exact match of the tsugite_id string.",
          "You want the content type inside application/gzip to be identified reliably. The content type is looked up from a table of file name extensions, so when the extension is not in the table or the name is misleading, it checks with a different type and the result may be need_input.",
          "You want to run an untrusted extractor in isolation. Extractors run in the same process and have no time or memory limit.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, it stops with INVALID_OPTIONS; when the input to probeFetch is malformed (the shape of folder_ref, fetch_failed not a boolean, and so on), with INVALID_INPUT; and when a getter throws an exception, with INSPECTION_FAILED.",
          "When the N-09 port throws or its answer is inconsistent (not an answer about this record, or the shape of schema, partition or files differs), it reports CHECK_PORT_FAILED, and when the record's source and folder_stamp differ from folder_ref, FOLDER_MISMATCH; and it does not write a result.",
          "In the integrity stage, it reports INTEGRITY_MISSING when the stored object does not exist, INTEGRITY_DIGEST_MISMATCH on GET_REJECTED or when the sha256 recomputed by the part itself differs, and INTEGRITY_SIZE_MISMATCH when the size differs from byte_size; it lists all files and sets need_input.",
          "When the result of a read in the integrity stage is unknown, it stops with READ_UNKNOWN; on a refusal, a halt, a malformed result or a value that is not bytes, with READ_FAILED; and when the I-09 port throws or its answer is malformed, with READABILITY_PORT_FAILED; and it does not write a result (errors[0].name gives the file).",
          "In the readability stage, if any file cannot be read, the result is need_input / UNREADABLE, and if any file was not checked, need_input / READABILITY_NOT_CHECKED. In the usability stage, when the extractor throws or answers with something other than an array, the reason is EXTRACTOR_FAILED; when there are 0 records, NO_RECORDS; and when any of the 3 items is missing, MISSING_FACTS (with the count and the names of the missing items).",
          "When this instance has previously sent different content for the result key of the same version, it does not send (PROBE_RESULT_EXISTS). When the result of a write is unknown, it reports PROBE_WRITE_UNKNOWN (PROBE_PENDING while that is pending); when the Tsugite did not send, WRITE_REFUSED; and when the Tsugite halted or the result is not APPLIED or DEDUP_HIT, WRITE_FAILED.",
          "When timestamp does not return an existing date-time in RFC 3339 UTC (Z), it reads and writes nothing (CLOCK_FAILED); when the previous operation has not finished, it reports BUSY; and calling resolvePending when nothing is pending returns NOTHING_PENDING."
        ],
        "what_it_does_not_do": [
          "The Fetched data probe part is not a Tsugite. It has no Request or Receipt of its own; the external effect of writing the result is caused by the bundled C-05 Tsugite under its own contract (reading the raw data is NONE). This part itself does not touch the outside.",
          "The meaning of the bundled parts (the record was accepted ≠ the fetch is correct, read ≠ the content is correct, opened ≠ not broken, written ≠ accepted) stays on each part's side. The extractors themselves and the correctness of the extracted values are outside this part.",
          "The result is not approval and does not show correctness (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "It does not write the extracted values into the result record. It writes only the counts, the names of the missing items, and file names and indexes.",
          "Whether a raw key points to a stored object in that partition is the job of the backend configuration. A result of the same version written by another process is refused by the Tsugite and the backend, not by this part (neither overwrites).",
          "In this part's tests it is not connected to the real N-09, I-09, C-01 or C-05. The unit tests and fuzzing use stand-ins. The state (the record of sent result keys, the pending write) is only inside the process and is lost on restart.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular data quality tool."
        ],
        "integration": "Create it with createProbe(options) in src/probe.js; it returns { ok, errors, instance }. options are probe_version, certified_tsugite, result_target, request_prefix, ports and an optional timestamp. probeFetch returns state, reason, result, result_key and other fields, and the results carry approval: false. Ports: checkFetchRecord (N-09), probeReadability (I-09), read.<class>.get for each partition (the client of the C-01 Tsugite), write.submit (the client of the C-05 Tsugite) and optional extractors. Credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "I-11",
        "category": "I/検証・無害化・安全",
        "kind": "Primitive",
        "title": "Selected input assembly and UTF-8 capacity — Selected input assembly and UTF-8 capacity part (Primitive)",
        "summary": "The Selected input assembly and UTF-8 capacity part (Primitive) is a standalone part that joins only explicitly selected items, in the order selected, with a fixed separator (prefix + pieces.join(separator) + suffix), counts the UTF-8 bytes, and compares them with a limit passed by the caller. A selection names the revision, and a range names its unit (UTF16 or CODE_POINT). If the limit is exceeded, it returns OVER_LIMIT and the amount over, and cuts nothing. Input it cannot accept is stopped with a fixed code and position (fail-closed).",
        "problem": "What a sent text was assembled from, and whether it was within the limit, cannot be seen afterwards. This part prevents adding items that were not selected, using a selection of an old revision, and silently cutting, shrinking or splitting a text that exceeds the limit, and it returns a list of what was selected, what was excluded, the revisions and the digests.",
        "use_when": [
          "You want to assemble a request by joining only explicitly selected items (naming id and revision), in the order selected, with separator, and adding prefix and suffix before and after (assemble). Items that were not selected are not included.",
          "You want to compare the UTF-8 bytes of the assembled request with a limit, limit_bytes (1 to 1,073,741,824), passed by the caller. Exactly at the limit is WITHIN_LIMIT. The part has no default limit; the caller passes it.",
          "You want to select only part of an item with a range that names its unit, { unit, start, end }. unit is UTF16 or CODE_POINT; since the same numbers can point to different characters, the unit is not guessed.",
          "You want to trace afterwards what was assembled. It returns request_digest (the sha256 of the request in UTF-8), selection_digest, which changes if any of the revision, content, range or separator changes, included in the order selected, and excluded for the items not selected.",
          "You want to fix the string to be sent after per-role projection (P-12). Unlike I-06, which measures the size of a single value, it assembles only what was selected and fixes the revisions and digests.",
          "You want to call it locally, as processing that does not throw an exception, does not modify its input, and has no state. The same input always gives the same answer."
        ],
        "do_not_use_when": [
          "You want a text over the limit cut, shrunk or split to make it fit. With OVER_LIMIT, request_text is null, and no piece is cut or removed. Selecting again is up to the caller.",
          "You want the part to check what is selected (the authority to select, consent, purpose). It only assembles what was selected; it does not look at whether the authority to select, consent or purpose is correct.",
          "You want to measure the size including an outer wrapper or frame such as JSON. It counts only prefix + pieces.join(separator) + suffix; the caller checks the outer wrapper separately.",
          "You want to send or store the assembled text. This part neither sends nor stores. Building the outer wrapper and sending remain with the caller.",
          "You want to handle input where items or selection has more than 10,000 entries, or text or a separator exceeds 4,194,304 UTF-16 code units. Beyond these limits it stops with a fixed code.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options is not a plain object, has an unknown name or a getter, or lacks limit_bytes or separator, it stops with INVALID_OPTIONS; when limit_bytes is not an integer from 1 to 1,073,741,824, with INVALID_LIMIT; and when separator, prefix or suffix is not a string (including boxed strings) or is too long, with INVALID_FRAMING.",
          "When items is not a plain array it stops with INVALID_ITEMS; when it has more than 10,000 entries, with TOO_MANY_ITEMS; when an element has the wrong shape, with MALFORMED_ITEM; when id, revision or text has the wrong form, with INVALID_ID, INVALID_REVISION or INVALID_TEXT; when text is too long, with TEXT_TOO_LONG; and when the same id appears twice, with DUPLICATE_ITEM.",
          "When selection has the wrong shape it stops with INVALID_SELECTION, TOO_MANY_SELECTED or MALFORMED_SELECTION; when a selected id is not in items, with UNKNOWN_ITEM; when the selected revision differs from the revision in items, with REVISION_MISMATCH (an old selection is not used); and when the same id is selected twice, with DUPLICATE_SELECTION.",
          "When range is not { unit, start, end }, unit is not UTF16 or CODE_POINT (including lower case), or start and end are not safe integers with 0 ≤ start < end, it stops with INVALID_RANGE; when end exceeds the length of text, with RANGE_OUT_OF_BOUNDS; and when an end of a UTF16 range falls inside a surrogate pair, with RANGE_SPLITS_SURROGATE.",
          "When a selected piece, or separator, prefix or suffix, contains a lone surrogate (it cannot be encoded as UTF-8) it stops with INVALID_UNICODE; when the assembled request is empty or only whitespace (including line breaks and full-width spaces), with BLANK_REQUEST; and when inspecting a value itself raises an exception (a Proxy trap and so on), with INSPECTION_FAILED.",
          "When it stops, it returns the code and position (path, a JSON Pointer) of the first problem found in a fixed order, and returns no assembled result.",
          "Fuzzing (1,000,000 runs) that compares the results against an independent oracle written in a different way (TextEncoder for bytes, Array.from for code points, isWellFormed for lone surrogates, and a front-to-back search of items without a Map) confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "WITHIN_LIMIT only means \"within the limit passed in\"; it does not mean that the text may be sent or that there is consent. The size of an outer wrapper (JSON, a frame) is not counted.",
          "The content digest (content_digest) is the digest of the text passed in; it does not look at whether that text matches the original material.",
          "Accepting exactly the limits of 10,000 entries and 4,194,304 code units was checked only by unit tests (the fuzzing covers only small inputs).",
          "It has no external boundary, no state, no clock and no random numbers, and neither sends nor stores. Side effect: NONE, and it uses no credentials.",
          "The result of assembling and counting is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The input and output forms were defined in v1; there is no existing implementation (new).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular prompt-assembly library."
        ],
        "integration": "Call assemble(items, selection, options) in src/selected-input.js (it does not throw). items is an array of { id, revision, text }, selection is an array of { id, revision, range? }, and options is { limit_bytes, separator, prefix?, suffix? }. Returns: { ok: true, errors: [], status, request_text, request_digest, selection_digest, included, excluded and so on } or { ok: false, errors: [{ code, path }] }. There is no side effect, no state, no external connection, no credential, no dependency package and no port.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-01",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "PDF generation — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind PDF generation. Before it passes a PDF generation request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend makes the PDF; this Tsugite does not make PDFs. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When an external conversion backend is used to output business forms and supporting documents as PDF, it prevents sending spoofed input or an unpinned output profile, and prevents passing on a returned PDF without checking that it is the one requested (swapping). It computes the digest of the output bytes itself and matches it against the receipt.",
        "use_when": [
          "You leave outputting business forms and supporting documents as PDF to an external conversion backend and want checks before and after it.",
          "You want to pin the PDF output profile and params so that PDFs are made only in the form you decided.",
          "You want to accept the PDF bytes returned by the backend only when they match the digest in the receipt.",
          "You do not want the same request to be sent twice. A resend after a failure is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want the PDF-making mechanism itself. This Tsugite does not make PDFs and does not include an actual backend.",
          "You want to check whether the resulting PDF looks the same as the original (fidelity) or whether it is intact as a PDF. Neither is checked.",
          "You want to store the resulting PDF. Storage is the role of the object store Tsugite (C-01).",
          "You need e-signatures. E-signature (J-06) has not been built, and this part does not cover J-06. Requests for an e-signature operation are not sent.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not generate PDFs itself. An external conversion backend makes the PDF; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the visual fidelity of a conversion. A successful conversion does not mean the meaning is the same.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a conversion engine."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-02",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "OCR and text extraction — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind OCR and text extraction. Before it passes an OCR request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend reads the text; this Tsugite does not perform OCR. Text that was read is treated neither as a verified fact nor as trusted. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When text is extracted from images or paper for search and matching preprocessing, it prevents sending spoofed input, treating a result as success when there is no extraction result (extracted_ref), and treating text that was read as if it were a checked fact.",
        "use_when": [
          "You leave reading text from images or PDFs to an external OCR service and want checks before and after it.",
          "You want every successful OCR_EXTRACT to carry extracted_ref. A success without it is not accepted.",
          "You want to receive text that was read as material for search and matching preprocessing, and leave the decision on whether to treat it as fact to the Kernels (evidence family).",
          "You do not want the same request to be sent twice. A resend after a failure is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to confirm that the text read is correct. The correctness of content read by OCR is not checked, and it is treated neither as a verified fact nor as trusted.",
          "You want an OCR engine itself. This Tsugite does not perform OCR and does not include an actual backend. Which OCR service to connect to has not been decided.",
          "You want to send input in a format that is not on the allowlist. The only types that can be confirmed by magic bytes are application/pdf, image/png, image/jpeg, text/plain and application/json; types not on this list are not sent.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not perform OCR itself. An external conversion backend reads the text; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the correctness of content read by OCR (as the contract's const specifies, it makes no such claim). Whether to treat text that was read as fact is decided by the evidence-family Kernels.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a conversion engine."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED (with extracted_ref for OCR), PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-03",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Format conversion — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind format conversion. Before it passes a format conversion request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend changes the format; this Tsugite does not convert. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When an external conversion backend changes a document's format for compatibility, it prevents sending spoofed input or an unpinned target (output profile), and prevents passing on the returned bytes without checking that they are the ones requested (swapping).",
        "use_when": [
          "You leave document format conversion to an external conversion backend and want checks before and after it.",
          "The input is application/pdf, image/png, image/jpeg, text/plain or application/json, and you want its type confirmed by its leading magic bytes before sending.",
          "You want to pin the target output profile and params.",
          "You want to accept the converted bytes only when they match the digest in the receipt."
        ],
        "do_not_use_when": [
          "You want to send a format not on the list, such as docx, as input. The only types that can be confirmed by magic bytes are application/pdf, image/png, image/jpeg, text/plain and application/json; types not on this list are not sent (the docx in the catalog one-liner \"docx→pdf\" is not on this list).",
          "You want to confirm that meaning or appearance is the same before and after conversion. A successful conversion does not mean the meaning is the same, and visual fidelity is not checked.",
          "You want a conversion engine itself. This Tsugite does not convert and does not include an actual backend.",
          "You want to store the converted result. Storage is the role of the object store Tsugite (C-01).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not perform format conversion itself. An external conversion backend changes the format; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the visual fidelity of a conversion. A successful conversion does not mean the meaning is the same.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a conversion engine."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-04",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Thumbnail/preview generation — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind thumbnail and preview generation. Before it passes a thumbnail request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend makes the thumbnails; this Tsugite does not make them. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When an external conversion backend makes the thumbnails or previews used for how lists look, it prevents sending spoofed or oversized input, and prevents passing a returned image on to display without checking that it is the one requested (swapping).",
        "use_when": [
          "You leave making the thumbnails or previews shown in lists to an external conversion backend and want checks before and after it.",
          "You want to pin the output profile and params so that thumbnails are made only in the form you decided.",
          "You want to set an upper limit on input size (max_input_bytes) and not send anything over it.",
          "You want to accept the image bytes returned by the backend only when they match the digest in the receipt."
        ],
        "do_not_use_when": [
          "You want the thumbnail-making mechanism itself. This Tsugite does not make thumbnails and does not include an actual backend.",
          "You want to check whether a thumbnail looks like the original (fidelity). This is not checked.",
          "You want to store the thumbnails made. Storage is the role of the object store Tsugite (C-01).",
          "You want to send input in a format that is not on the allowlist. The only types that can be confirmed by magic bytes are application/pdf, image/png, image/jpeg, text/plain and application/json; types not on this list are not sent.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not make thumbnails or previews itself. An external conversion backend makes them; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the visual fidelity of a conversion. A successful conversion does not mean the meaning is the same.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a conversion engine."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-05",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Image optimization — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind image optimization. Before it passes an image optimization request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend downscales and compresses images; this Tsugite does not change them. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When an external conversion backend downscales and compresses images for display speed, it prevents sending spoofed input or unpinned settings, and prevents using a returned image without checking that it is the one requested (swapping).",
        "use_when": [
          "You leave downscaling and compressing images to an external conversion backend and want checks before and after it.",
          "The input is an allowed type such as image/png or image/jpeg, and you want its type confirmed by its leading magic bytes before sending.",
          "You want to pin the optimization output profile and params.",
          "You want to accept the image bytes returned by the backend only when they match the digest in the receipt."
        ],
        "do_not_use_when": [
          "You want the mechanism that downscales and compresses images itself. This Tsugite does not change images and does not include an actual backend.",
          "You want to check whether an optimized image looks like the original (fidelity). This is not checked.",
          "You want to store optimized images. Storage is the role of the object store Tsugite (C-01).",
          "You want to send input in a format that is not on the allowlist. The only types that can be confirmed by magic bytes are application/pdf, image/png, image/jpeg, text/plain and application/json; types not on this list are not sent.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not perform image optimization itself. An external conversion backend downscales and compresses the image; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the visual fidelity of a conversion. A successful conversion does not mean the meaning is the same.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a conversion engine."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-06",
        "category": "J/文書・メディア処理",
        "kind": "Component",
        "title": "Electronic contract (envelope) — Electronic contract (envelope) part (Component)",
        "summary": "The Electronic contract (envelope) part (Component) is a part that bundles a signing flow. It fixes the document's digest (B-07), checks each designated signer's signature with a pinned key (B-02), and advances the state only when the state transition (open, sign, complete, void, timestamp) has been recorded in the audit ledger Tsugite (H-01). Checking a time token attached to the completion digest (B-06) is bundled optionally. When the ledger's result is unknown, it does not advance the state and holds the record as pending (fail-closed). Signatures being complete is not legal acceptance, authority or approval.",
        "problem": "As a layer separate from B signing, in a signing flow it prevents claiming a state that has no record in the ledger, reusing a signature made for another envelope, document or signer, advancing the state while the ledger's result is unknown, and passing the document's body to the ledger.",
        "use_when": [
          "You want to collect the signatures of the designated signers (1 to 20) on one document, and set COMPLETED only when every signer's signature has passed the check and been recorded.",
          "You want each signer's signature checked by the B-02 part against public keys fixed by the caller (pinned_keys). Each signer signs the bytes returned by signingBytes(signer_ref) (which include envelope_ref, the document's digest and signer_ref).",
          "You want to append state transitions to the H-01 audit ledger Tsugite as AuditEventEnvelope, and make only transitions that were recorded (APPENDED_VERIFIED or ALREADY_APPENDED) into the state. Only the digest of the transition's payload is passed to the ledger.",
          "After completion, you want to check a time token attached to the completion digest once, with a function that bundles B-06's verifyToken, and record it (optional).",
          "You want to resend a record whose ledger result was unknown, with the same envelope (resolvePending)."
        ],
        "do_not_use_when": [
          "You want to use signatures being complete or a time being attached as legal acceptance, authority, delegation or approval. \"Signed\" shows only that the holder of a pinned key signed the bytes for this envelope, this document and this signer.",
          "You need identity verification of the signers. This part does not handle identity verification, authority or delegation, or the legal effect of signatures.",
          "You want it to also send signing requests (notifications). Notifications are not bundled. If they are to be sent, the caller sends them with the D-01 Tsugite.",
          "You want it to also obtain the time token (a request to a TSA). Obtaining it is not included, and B-06 only checks.",
          "You want to connect to an external electronic signature service. That would need the contract of a separate Tsugite, which is not being built now.",
          "You want to continue a flow across a restart. The state is only inside this process, and v1 has no function to rebuild the state from the ledger's records.",
          "You want to create two flows with the same envelope_ref. The event_id values would collide and the ledger refuses them (v1 has one flow per envelope_ref).",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed (an invalid reference, a duplicate signer or key, a key not in the pinned set, 21 or more signers, an extra key in the ledger settings, a getter), it stops with INVALID_OPTIONS.",
          "When an operation is not possible in the current state (signing before open or after void, voiding after completion, a timestamp before completion or a second time), it stops with WRONG_STATE, and when the previous operation has not finished, with BUSY.",
          "When the signer is not a designated signer it stops with UNKNOWN_SIGNER, when that signer has already signed with ALREADY_SIGNED, and when the signature does not pass the B-02 check (bytes for another envelope, document or signer, or another key) with SIGNATURE_REJECTED.",
          "When the ledger's result is unknown (UNKNOWN, an exception, no reply), it does not advance the state and holds the record as pending with LEDGER_UNKNOWN; while it is pending, anything other than resolvePending is stopped with RECORD_PENDING. Calling resolvePending when nothing is pending returns NOTHING_PENDING.",
          "When the ledger refuses (REFUSED, RECEIPT_REJECTED, HALTED and so on) or the shape of the ledger's head is invalid, it stops with LEDGER_REFUSED without advancing the state and without keeping a pending record (if the head is invalid, it does not send).",
          "When the timestamp request is not for the completion digest, hash_algo is not \"SHA256\", or it does not pass the B-06 check, it stops with TIMESTAMP_REJECTED, and when no verifyTimestamp function has been injected, with NOT_CONFIGURED.",
          "When the ledger's state() or an injected check function throws an exception it stops with PORT_FAILED, when a getter on an injected function's result throws with INSPECTION_FAILED, when the result of contentAddress is invalid with DIGEST_FAILED, and when timestamp is invalid with CLOCK_FAILED."
        ],
        "what_it_does_not_do": [
          "The Electronic contract (envelope) part is not a Tsugite. It has no Request or Receipt of its own; the external effect of recording in the ledger is caused by the bundled H-01 Tsugite under its own contract (audit-ledger/v1). This part itself does not touch the outside.",
          "The meaning of the bundled parts (digest identity, checking with a pinned key, matching the ledger's receipt, checking the time token; none of them is approval) stays on each part's side.",
          "Signatures being complete or a time being attached is not legal acceptance, authority, delegation or approval (approval: false, authority: NOT_DERIVED, derives_authority: false).",
          "It does not handle identity verification of the signers, authority or delegation, or the legal effect of signatures.",
          "It does not include notifications (sending signing requests) or obtaining the time token (a request to a TSA).",
          "It has never connected to a real ledger or TSA. The unit tests and fuzzing use stand-ins, and signature verification is also a stand-in (cryptographic correctness is the role of B-02). The wiring with the code of the real four parts was checked against a local stand-in backend.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular electronic signature service."
        ],
        "integration": "Create it with createEnvelopeFlow(options) in src/envelope.js. Input: envelope_ref, signers, pinned_keys, ledger, ports and so on. Returns: { ok, errors, instance }; each operation returns { ok, errors, approval: false, phase, signed, … }. User provides (injected through ports): contentAddress (B-07), verifySignature (B-02), ledger.append and state (the client of the H-01 Tsugite), and an optional verifyTimestamp (B-06). Side effect: caused by the bundled Tsugite (EXTERNAL_IRREVERSIBLE); credentials are held by the injected functions.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-07",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Barcode/QR generation and reading — Document and Media Transform Tsugite",
        "summary": "Of the barcode and QR generation and reading item, the Document and Media Transform Tsugite (継手, tsugite) handles only QR reading (QR_DECODE) requests. Before it passes an image to an external conversion backend, it checks the input and the operation, and it then matches the returned receipt and output. The backend reads the QR code; this Tsugite does not. QR generation is not in the contract, and content that was read is treated neither as a verified fact nor as trusted (fail-closed).",
        "problem": "When QR codes are read for on-site input and output, it prevents sending spoofed images, treating a result as success when there is no extraction result (extracted_ref), and treating content that was read as if it were a checked fact.",
        "use_when": [
          "You leave QR reading to an external conversion backend and want checks before and after it.",
          "You want every successful QR_DECODE to carry extracted_ref. A success without it is not accepted.",
          "You want to receive content that was read and leave the decision on whether to treat it as fact to the Kernels (evidence family).",
          "You do not want the same request to be sent twice. A resend after a failure is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to generate QR codes. QR generation is not in the contract; this Tsugite handles only reading (QR_DECODE).",
          "You want to handle barcodes. The operations of this part list QR reading; barcodes are not listed.",
          "You want to confirm that the content read is correct. The correctness of content read from QR codes is not checked, and it is treated neither as a verified fact nor as trusted.",
          "You want the QR-reading mechanism itself. This Tsugite does not read QR codes and does not include an actual backend.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not generate QR codes. That is not in the contract; it handles only QR_DECODE reading requests.",
          "It does not read QR codes itself either. An external conversion backend reads them; this Tsugite only checks before sending and matches after receiving.",
          "It does not check the correctness of content read from QR codes (as the contract's const specifies, it makes no such claim).",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED (with extracted_ref for QR), PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-08",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Watermarks and stamps — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind watermarks and stamps. Before it passes a watermark request to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend applies the watermark; this Tsugite does not apply watermarks. If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When an external conversion backend applies a watermark as a mark of origin or confidentiality, it prevents sending spoofed input or unpinned settings, and prevents passing on the returned output without checking that it is the one requested (swapping).",
        "use_when": [
          "You leave watermarking documents or images to an external conversion backend and want checks before and after it.",
          "You want to pin the watermark output profile and params so that watermarks are applied only in the form you decided.",
          "You want to accept the output bytes returned by the backend only when they match the digest in the receipt.",
          "You do not want the same request to be sent twice. A resend after a failure is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You need e-signatures. E-signature (J-06) has not been built, and this part does not cover J-06. Requests for an e-signature operation are not sent.",
          "You want to decide what to mark or what to keep confidential. That decision is the role of the Kernel (redact-facts) and people; this Tsugite only checks before sending and after receiving.",
          "You want to use watermarked output as grounds for trust or for disclosure authorization. Every result is trust: NOT_DERIVED, and no disclosure authorization is derived.",
          "You want the watermarking mechanism itself. This Tsugite does not apply watermarks and does not include an actual backend.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not apply watermarks itself. An external conversion backend applies them; this Tsugite only checks before sending and matches after receiving.",
          "It does not do e-signatures. E-signature (J-06) has not been built.",
          "It does not check the visual fidelity of a conversion (whether the watermark is actually applied as it appears).",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "J-09",
        "category": "J/文書・メディア処理",
        "kind": "Tsugite",
        "title": "Redaction rendering — Document and Media Transform Tsugite",
        "summary": "The Document and Media Transform Tsugite (継手, tsugite) works behind redaction rendering. Before it passes a request to draw redactions to an external conversion backend, it checks the input, the operation and the output profile, and it then matches the returned receipt and output bytes. The backend draws the redactions, and the Kernel (redact-facts) and people decide what to redact. This Tsugite only passes on the rendering request and matches the result; it does not authorize disclosure (fail-closed).",
        "problem": "When an external conversion backend renders the redactions decided by the Kernel (redact-facts), it prevents sending spoofed input, accepting a receipt that claims \"disclosure authorized\", and passing on the returned output without checking that it is the one requested (swapping).",
        "use_when": [
          "You leave rendering the redactions decided by the Kernel (redact-facts) and people to an external conversion backend and want checks before and after it.",
          "You do not want the rendering result mistaken for disclosure authorization. A receipt whose disclosure_authorization is anything other than NOT_DERIVED is not accepted.",
          "You want to accept the output bytes returned by the backend only when they match the digest in the receipt.",
          "You do not want the same request to be sent twice. A resend after a failure is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to decide what to redact. Deciding what to redact is the role of the Kernels and people; this Tsugite only passes on the rendering request.",
          "You want to judge whether redacted output may be disclosed. Rendering a redaction is not disclosure authorization.",
          "You want to check whether the redaction is actually drawn as it appears (fidelity). This is not checked.",
          "You want the redaction-drawing mechanism itself. This Tsugite does not render and does not include an actual backend.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It does not accept a receipt that claims to be verified, disclosure-authorized or trusted (any value other than content_verification NOT_VERIFIED, fidelity_assertion NOT_ASSERTED, disclosure_authorization NOT_DERIVED or trust_assertion NOT_DERIVED).",
          "It checks the input. Input whose bytes do not match the digest in the request, input over the limit (max_input_bytes), input with a content-type that is not allowed, and input whose leading magic bytes do not match its content-type (spoofed input) are not sent to the conversion backend.",
          "It checks the operation, output profile and params. Requests for an operation that is not allowed, for an e-signature (J-06) operation, or for an output profile or params that are not pinned are not sent.",
          "It computes the digest of the output bytes itself. If it differs from the receipt, the output is not accepted (OUTPUT_REJECTED) and it stops from then on (swapped output is not taken).",
          "It checks the receipt (MediaTransformReceipt). A spoofed request_digest, a receipt for another request, a swapped operation, or a receipt that increases the external effect is not accepted, and it stops from then on (HALTED).",
          "It checks the shape of the result. It does not accept SUCCEEDED without an output digest, SUCCEEDED for OCR_EXTRACT or QR_DECODE without extracted_ref, or FAILED that comes with an output or an effect. PARTIAL is not treated as success.",
          "It never sends the same request_id twice. A backend failure is treated as UNKNOWN, and a resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not render redactions itself. An external conversion backend draws them; this Tsugite only checks before sending and matches after receiving.",
          "It does not decide what to redact and does not authorize disclosure. Deciding what to redact is the role of the Kernel (redact-facts) and people.",
          "It does not check the visual fidelity of a conversion.",
          "It does not include an actual backend. Which conversion engine or OCR service to connect to, the port implementation and the profiles to pin are undecided and will be settled at a separate gate when connecting.",
          "Magic bytes are checked only in the first few bytes. It does not inspect the structure of the whole file (for example, a broken PDF).",
          "No result is approval (approval: false). Every result carries content_verification: NOT_VERIFIED and trust: NOT_DERIVED.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/document-media/v1. Input: call transform(request, bytes) on createMediaTransformClient({ cfg, port }) with the request and the input bytes. Returns: one of TRANSFORMED, PARTIAL, FAILED, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED, OUTPUT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the conversion backend (transform, and optionally lookup) and cfg (allowlists, pins and limits). Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-01",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Structured logging — Observability Sink Tsugite",
        "summary": "The Observability Sink Tsugite (継手, tsugite) takes batches of machine-readable structured logs (records of ts, level, event and attrs), checks the allowed events and keys, the types, the absence of values that look sensitive, the redaction record and the digest, then sends them to a sink and matches the receipt it returns (SinkReceipt). If anything does not match, it does not send and does not accept (fail-closed). Records the sink did not accept are returned as missing.",
        "problem": "When logs are streamed to an external sink for incident investigation, it prevents sensitive values such as email addresses or credentials, or free text, from getting into logs and being copied to the sink, and prevents records the sink did not accept from silently disappearing. It sends only short primitive values under allowed keys, and reports missing records with SUNK_PARTIAL and dropped.",
        "use_when": [
          "You want to send structured logs in the form ts, level, event and attrs to an external sink as machine-readable records.",
          "You want to decide in advance which events (log_events) and keys (log_attrs) may appear in logs, and send nothing else.",
          "You do not want sensitive values in logs copied to the sink. The redaction record (redaction_manifest_ref) can be made mandatory.",
          "You want logs the sink did not accept reported as a fact of missing records, not silently dropped.",
          "You do not want the same batch streamed twice. The same request_id is not sent again (ALREADY_SUNK)."
        ],
        "do_not_use_when": [
          "You want to keep free-text messages or long bodies in logs as they are. There is no free-text field; only short primitive values under allowed keys are sent.",
          "You need error tracking (K-04) or alerting (K-06). Those are separate parts and are not included in this Tsugite.",
          "You want a sink you can use right away. No actual sink (a provider's log intake) is included, and it has not been decided.",
          "You want to treat the sink's accepted count as verification that log contents are correct. An observation is not a verified fact.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks for values that look sensitive. If a batch contains email addresses, runs of 10 or more digits, Bearer tokens, JWTs, keys or text that looks like credentials, it is not sent (REFUSED).",
          "Records with free-text fields, or with events or keys (attrs) that are not allowed, are not sent.",
          "It checks sizes. Anything over the limits on batch size (max_payload_bytes), number of records (max_records) or value length (max_attr_len) is not sent.",
          "It checks the pinned sink. Nothing is sent to a sink that is not pinned. If the payload has been tampered with (payload_digest mismatch), it is not sent.",
          "It requires the redaction record (redaction_manifest_ref) and does not send anything without it. This is stricter than the contract.",
          "It checks the receipt (SinkReceipt). A receipt with a spoofed request digest, or one accepting more records than were sent, is not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A sink failure (a 500 response) is treated as UNKNOWN, and a resend is resolved by a lookup. If the receipt's observation_state is UNKNOWN, it is not matched."
        ],
        "what_it_does_not_do": [
          "Deciding the log structure is the role of the Kernel policy-facts, and deciding minimization is the role of the Kernel redact-facts. This Tsugite only checks in front of the sink.",
          "Detection of sensitive values is an approximation based on character patterns. Free words such as names or addresses are only held back by allowed keys and short values; they are not recognized.",
          "It does not include an actual sink. Which intake to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "An observation is not a verified fact. verified becomes true only when the receipt shows VERIFIED and a verifier. Success is not approval (approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED). The shape of the payload contents is defined by the Tsugite, not by the contract.",
          "Besides K-01, this part also serves K-02 (metrics) and K-03 (distributed tracing) as one and the same part, and the tests and inspection are shared. The per-ID rows (K-01, K-02 and K-03) point to this part.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a monitoring service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1 (SinkRequest / SinkReceipt). Input: pass a SinkRequest and the log { records: [...] } as bytes to sink(SinkRequest, bytes) on createSinkClient({ cfg, port }). Returns: one of SUNK, SUNK_PARTIAL, ALREADY_SUNK, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the sink (write, and optionally lookup) and cfg (the pinned sink, the allowed events and keys, and the limits). Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-02",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Metrics — Observability Sink Tsugite",
        "summary": "The Observability Sink Tsugite (継手, tsugite) takes batches of metrics, which are continuous numeric measurements (records of ts, name, type, value and labels), checks the allowed metrics and labels, the types, the absence of values that look sensitive, the redaction record and the digest, then sends them to a sink and matches the accepted count in the receipt it returns (SinkReceipt). If anything does not match, it does not send and does not accept (fail-closed). Records the sink did not accept are returned as missing.",
        "problem": "When metrics are streamed continuously to an external sink, it prevents metrics or labels that were never decided from creeping in, sensitive values in labels from being copied to the sink, and numbers the sink did not accept from silently disappearing. It stops on receipt anomalies such as accepting more records than were sent.",
        "use_when": [
          "You want to send metrics in the form ts, name, type, value and labels to an external sink as continuous numeric measurements.",
          "You want to decide in advance which metrics (metrics) and labels (metric_labels) may be sent, and send nothing else.",
          "You want numbers the sink did not accept reported as a fact of missing records (SUNK_PARTIAL and dropped), not silently dropped.",
          "You want a receipt that reports accepting more records than were sent to be treated as an anomaly, and further sending to stop.",
          "You do not want the same batch streamed twice. The same request_id is not sent again (ALREADY_SUNK)."
        ],
        "do_not_use_when": [
          "You want to judge metric thresholds or raise alerts. Threshold facts are the role of the Kernel monitor-detect, and alerting (K-06) is a separate part.",
          "You want to put free text or long values in labels. There is no free-text field; only short primitive values under allowed labels are sent.",
          "You want a sink you can use right away. No actual sink (a provider's metrics intake) is included, and it has not been decided.",
          "You want to treat the sink's accepted count as verification that measured values are correct. An observation is not a verified fact.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Records with metrics or labels that are not allowed, or with free-text fields, are not sent (REFUSED).",
          "It checks for values that look sensitive. If a batch contains email addresses, runs of 10 or more digits, Bearer tokens, JWTs, keys or text that looks like credentials, it is not sent.",
          "It checks sizes. Anything over the limits on batch size (max_payload_bytes), number of records (max_records) or value length (max_attr_len) is not sent.",
          "It checks the pinned sink. Nothing is sent to a sink that is not pinned. If the payload has been tampered with (payload_digest mismatch), it is not sent.",
          "It matches the accepted count in the receipt (SinkReceipt). A receipt accepting more records than were sent, or one with a spoofed request digest, is not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "It requires the redaction record (redaction_manifest_ref) and does not send anything without it. This is stricter than the contract.",
          "A sink failure (a 500 response) is treated as UNKNOWN, and a resend is resolved by a lookup. If the receipt's observation_state is UNKNOWN, it is not matched."
        ],
        "what_it_does_not_do": [
          "It does not judge thresholds. Metric threshold facts are the role of the Kernel monitor-detect, and minimization decisions are the role of the Kernel redact-facts; this Tsugite only checks in front of the sink.",
          "Detection of sensitive values is an approximation based on character patterns. Free words such as names or addresses are only held back by allowed labels and short values; they are not recognized.",
          "It does not include an actual sink. Which intake to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "An observation is not a verified fact. verified becomes true only when the receipt shows VERIFIED and a verifier. Success is not approval (approval: false).",
          "It is not a part dedicated to K-02. It is one and the same part as K-01 (structured logging) and K-03 (distributed tracing), and the tests and inspection are shared. The per-ID rows (K-01, K-02 and K-03) point to this part.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED). The shape of the payload contents is defined by the Tsugite, not by the contract.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a monitoring service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1 (SinkRequest / SinkReceipt). Input: pass a SinkRequest and the metrics { records: [...] } as bytes to sink(SinkRequest, bytes) on createSinkClient({ cfg, port }). Returns: one of SUNK, SUNK_PARTIAL, ALREADY_SUNK, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the sink (write, and optionally lookup) and cfg (the pinned sink, the allowed metrics and labels, and the limits). Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-03",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Distributed tracing — Observability Sink Tsugite",
        "summary": "The Observability Sink Tsugite (継手, tsugite) takes batches of traces used to follow processing paths (spans of trace_id, span_id, parent_span_id, name, start_ts, end_ts and attrs), checks the span correlation (the same trace, exactly one root span, and every parent in the same batch), the allowed keys, the absence of values that look sensitive, the redaction record and the digest, then sends them to a sink and matches the receipt it returns (SinkReceipt). If anything does not match, it does not send and does not accept (fail-closed).",
        "problem": "When traces are streamed to an external sink to find the cause of latency, it prevents sending traces with broken correlation (spans from another trace mixed in, spans whose parent cannot be found, multiple roots) or traces that are only a fragment from the middle. It also refuses to let sensitive values mixed into span attrs be copied to the sink.",
        "use_when": [
          "You want to send spans in the form trace_id, span_id, parent_span_id, name, start_ts, end_ts and attrs to an external sink to trace processing paths.",
          "You want to send a trace only as a complete unit (exactly one root span, with every parent in the same batch).",
          "You want every trace sent with a correlation reference (correlation_ref). For traces, correlation_ref is required.",
          "You want to decide in advance which keys (span_attrs) may appear on spans, and send nothing else.",
          "You want spans the sink did not accept reported as a fact of missing records (SUNK_PARTIAL and dropped), not silently dropped."
        ],
        "do_not_use_when": [
          "You want to send only a fragment from the middle of a trace. Only batches with exactly one root span and every parent in the same batch are sent.",
          "You want to leave the final judgement on whether trace linkage is correct to this Tsugite. Trace linkage is the role of the Kernel provenance-facts.",
          "You need error tracking (K-04) or alerting (K-06). Those are separate parts and are not included in this Tsugite.",
          "You want a sink you can use right away. No actual sink (a provider's trace intake) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks trace correlation. If spans from another trace are mixed in, or a span ends before it starts, the batch is not sent (REFUSED).",
          "It checks the number of root spans and where parents are. A batch with multiple roots, or containing a span whose parent is outside the batch (orphan), is not sent.",
          "A trace without a correlation reference (correlation_ref) or a redaction record (redaction_manifest_ref) is not sent. Both are stricter than the contract.",
          "It checks span attrs. Anything with keys that are not allowed, free-text fields, or email addresses, runs of 10 or more digits, Bearer tokens, JWTs, keys or text that looks like credentials is not sent.",
          "It checks size and destination. Anything over the limits on batch size, number of records or value length, addressed to a sink that is not pinned, or with a tampered payload (payload_digest mismatch) is not sent.",
          "It checks the receipt (SinkReceipt). A receipt with a spoofed request digest, or one accepting more records than were sent, is not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A sink failure (a 500 response) is treated as UNKNOWN, and a resend is resolved by a lookup. The same request_id is not streamed twice (ALREADY_SUNK)."
        ],
        "what_it_does_not_do": [
          "Deciding trace linkage is the role of the Kernel provenance-facts, and deciding minimization is the role of the Kernel redact-facts. This Tsugite only checks in front of the sink.",
          "Detection of sensitive values is an approximation based on character patterns. Free words such as names or addresses are only held back by allowed keys and short values; they are not recognized.",
          "It does not include an actual sink. Which intake to connect to, the port implementation and what to pin are undecided and will be settled at a separate gate when connecting.",
          "An observation is not a verified fact. verified becomes true only when the receipt shows VERIFIED and a verifier. Success is not approval (approval: false).",
          "It is not a part dedicated to K-03. It is one and the same part as K-01 (structured logging) and K-02 (metrics), and the tests and inspection are shared. The per-ID rows (K-01, K-02 and K-03) point to this part.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED). The shape of the payload contents is defined by the Tsugite, not by the contract.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a monitoring service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/request-receipt-group/v1 (SinkRequest / SinkReceipt). Input: pass a SinkRequest and the span { records: [...] } as bytes to sink(SinkRequest, bytes) on createSinkClient({ cfg, port }). Returns: one of SUNK, SUNK_PARTIAL, ALREADY_SUNK, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the sink (write, and optionally lookup) and cfg (the pinned sink, the allowed span keys, and the limits). Side effect: EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-04",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Error tracking — Observability Extension Tsugite",
        "summary": "The Observability Extension Tsugite (継手, tsugite), as K-04 error tracking, checks an error-tracking request (ErrorTrackRequest) against the pinned destinations and a failure reference (failure_ref) that can be resolved before carrying it to the backing service, and matches the returned receipt against the contract. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When error records are sent to an external error-tracking backing service, it prevents sending to a destination that has not been decided, sending a failure reference that cannot be resolved, and treating the record as \"recorded\" without matching the backing service's receipt. It is a check so that a successful record is not treated as a verified fact.",
        "use_when": [
          "You want to send records of failures such as \"it crashes now and then\" to an error-tracking backing service.",
          "You want to decide in advance which error-tracking destinations (sink_refs) may be used, and send to nothing else.",
          "You want to send a failure reference (failure_ref) only when it can be resolved to an ai-execution-error record.",
          "You do not want the same error record sent twice. The same request_id is never sent twice, and a resend is resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to collect the exceptions themselves. This Tsugite carries error-tracking requests and does not include a function for collecting exceptions.",
          "You want to treat a successful record as a verified fact or as approval. The DONE result is verified_fact: NOT_ASSERTED and approval: false.",
          "You want the contents of the record that failure_ref points to checked as well. This Tsugite checks only whether it can be resolved.",
          "You want a backing service you can use right away. No actual backing service (which error tracking, monitoring, notification, flag or metering service) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Requests to a destination that is not pinned are not sent.",
          "A request carrying failure_ref is not sent (REFUSED) when the port's resolveFailureRef cannot resolve it to an ai-execution-error record, or when there is no means of resolving it.",
          "It does not send when a reference is empty, contains a path or \"..\" (path traversal), or has additional fields. Only references in the pinned lists are allowed, and an empty list allows nothing.",
          "The same request_id is never sent twice (ALREADY_DONE). A request with the same idempotency key but different content is not sent (REFUSED), and a duplicate (DEDUP_HIT) receipt that claims a new effect is not accepted.",
          "It matches the receipt. A spoofed request_digest, a receipt that increases the external effect, a receipt whose effect is unknown (UNKNOWN) and a receipt for a different request are not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A backing-service failure is returned as UNKNOWN. A resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not include a function for collecting exceptions. It only checks error-tracking requests, carries them to the backing service and matches the receipt.",
          "It does not check the contents of the ai-execution-error record that failure_ref points to (only whether it can be resolved).",
          "It does not treat a successful record as a verified fact (verified_fact: NOT_ASSERTED).",
          "A successful record is not approval (approval: false).",
          "Judging authorization, semantic correctness and business facts is the role of the Kernels (monitor-detect and policy-facts) and of people. This Tsugite only checks before sending and matches after receiving.",
          "It does not include an actual backing service. Which service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/observability-ext/v1. Returns: submit(request) on createObservabilityExtClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. DONE carries track_state, observation_state and verified_fact: NOT_ASSERTED in its result. User provides: cfg (the pinned sink_refs and the other lists) and a port (submit, optionally lookup, and resolveFailureRef for failure_ref). Side effect: at most EXTERNAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-05",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Health check — Observability Extension Tsugite",
        "summary": "The Observability Extension Tsugite (継手, tsugite), as K-05 health check, checks a health-check request (HealthCheckRequest) against the pinned targets before carrying it to the backing service, and matches the returned receipt against the contract. It does not mistake a health PASS for semantic correctness, and if any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When checking liveness and readiness, it prevents querying a target that has not been decided, and misreading a health PASS as \"the contents are correct too\". A receipt that claims semantic correctness is not accepted.",
        "use_when": [
          "You want to carry liveness and readiness health-check requests to the backing service.",
          "You want to decide in advance which targets (health_targets) may be queried, and query nothing else.",
          "You want to receive the health result (health_state) and semantic correctness (semantic_correctness) separately."
        ],
        "do_not_use_when": [
          "You want to use a health PASS as proof that processing results are semantically correct. This Tsugite does not derive semantic correctness from a PASS.",
          "You want to detect anomalies and recover automatically. This Tsugite carries health-check requests and does not include a recovery function.",
          "You want to judge anomalies by thresholds. Thresholds and the facts of detection are the role of the Kernel (monitor-detect).",
          "You want a backing service you can use right away. No actual backing service (which error tracking, monitoring, notification, flag or metering service) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Health-check requests to a target that is not pinned are not sent.",
          "A health receipt that claims semantic correctness is not accepted.",
          "The effect of a health check is NONE. A receipt that claims an external effect is not accepted, as it increases the effect.",
          "It does not send when a reference is empty, contains a path or \"..\" (path traversal), or has additional fields. Only references in the pinned lists are allowed, and an empty list allows nothing.",
          "The same request_id is never sent twice (ALREADY_DONE). A request with the same idempotency key but different content is not sent (REFUSED), and a duplicate (DEDUP_HIT) receipt that claims a new effect is not accepted.",
          "It matches the receipt. A spoofed request_digest, a receipt that increases the external effect, a receipt whose effect is unknown (UNKNOWN) and a receipt for a different request are not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A backing-service failure is returned as UNKNOWN. A resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not derive semantic correctness from a health PASS.",
          "It does not include a recovery function. It only checks health-check requests, carries them to the backing service and matches the receipt.",
          "A health result is not approval (approval: false).",
          "Judging authorization, semantic correctness and business facts is the role of the Kernels (monitor-detect and policy-facts) and of people. This Tsugite only checks before sending and matches after receiving.",
          "It does not include an actual backing service. Which service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/observability-ext/v1. Returns: submit(request) on createObservabilityExtClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. DONE carries health_state and semantic_correctness in its result. User provides: cfg (the pinned lists such as health_targets) and a port (submit, and optionally lookup). Side effect: NONE for health checks.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-06",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Alerting and on-call — Observability Extension Tsugite",
        "summary": "The Observability Extension Tsugite (継手, tsugite), as K-06 alerting and on-call, checks an alert request (AlertRequest) against the pinned alerts and destinations before carrying it to the backing service, and matches the returned receipt against the contract. It returns a delivered alert and an acknowledged alert separately, and if any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When reporting anomalies, it prevents sending alerts to a destination that has not been decided or to an arbitrary URL, and misreading \"delivered\" as \"acknowledged\".",
        "use_when": [
          "You want to carry anomaly-report (alert) requests to the backing service.",
          "You want to pin alerts (alert_refs) and destinations (alert_destinations) so that nothing is sent to an arbitrary URL.",
          "You want to receive delivered (delivered) and acknowledged (acknowledged) separately."
        ],
        "do_not_use_when": [
          "You want to confirm that an alert actually reached a person or was acted on. This Tsugite only returns the receipt's state and does not confirm it.",
          "You want to build on-call rotations or escalate calls step by step. This Tsugite carries alert requests and does not include those functions.",
          "You want to judge anomalies by thresholds. Thresholds and the facts of detection are the role of the Kernel (monitor-detect), and the alert policy is the role of the Kernel (policy-facts).",
          "You want a backing service you can use right away. No actual backing service (which error tracking, monitoring, notification, flag or metering service) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Requests for an alert that is not pinned, or to a destination that is not pinned, are not sent. Requests that inject an arbitrary URL as the destination (destination injection) are not sent either.",
          "delivered is allowed only when the state is DELIVERED or ACKNOWLEDGED, and acknowledged only when it is ACKNOWLEDGED. A receipt that claims delivery without an external effect is not accepted.",
          "It does not send when a reference is empty, contains a path or \"..\" (path traversal), or has additional fields. Only references in the pinned lists are allowed, and an empty list allows nothing.",
          "The same request_id is never sent twice (ALREADY_DONE). A request with the same idempotency key but different content is not sent (REFUSED), and a duplicate (DEDUP_HIT) receipt that claims a new effect is not accepted.",
          "It matches the receipt. A spoofed request_digest, a receipt that increases the external effect, a receipt whose effect is unknown (UNKNOWN) and a receipt for a different request are not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A backing-service failure is returned as UNKNOWN. A resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not confirm whether an alert actually reached a person or was acted on. It only returns the receipt's state.",
          "It does not include on-call rotation or call functions. It only checks alert requests, carries them to the backing service and matches the receipt.",
          "Delivering an alert is not approval (approval: false).",
          "Judging authorization, semantic correctness and business facts is the role of the Kernels (monitor-detect and policy-facts) and of people. This Tsugite only checks before sending and matches after receiving.",
          "It does not include an actual backing service. Which service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/observability-ext/v1. Returns: submit(request) on createObservabilityExtClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. DONE carries alert_state, delivered and acknowledged in its result. User provides: cfg with the pinned lists (such as alert_refs and alert_destinations) and a port (submit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE (NONE for DEDUP_HIT and FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-07",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Feature flags — Observability Extension Tsugite",
        "summary": "The Observability Extension Tsugite (継手, tsugite), as K-07 feature flags, checks a feature-flag request (FeatureFlagRequest: READ, SET or CLEAR) against the pinned flags and values before carrying it to the backing service, and matches the returned receipt against the contract. It does not derive authorization from a flag's state, and if any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When reading and writing feature flags to switch features for different users, it prevents setting flags or values that have not been decided, and misreading a set flag as \"authorized\". A flag receipt that claims authorization is not accepted.",
        "use_when": [
          "You want to carry READ, SET and CLEAR feature-flag requests to the backing service.",
          "You want to decide in advance which flags (flag_refs) and values (flag_value_refs) may be handled, and handle nothing else.",
          "You want to receive the flag state (flag_state) and authorization (authorization) separately."
        ],
        "do_not_use_when": [
          "You want to use a flag's state as authorization for a user or a feature. This Tsugite does not derive authorization from a flag's state.",
          "You want to receive the flag's value itself with READ. The value is not in the receipt, and this Tsugite returns only the state.",
          "You want to decide the percentage or targets of a staged rollout. This Tsugite carries feature-flag requests and does not include a function for deciding who gets which feature. The flag policy is the role of the Kernel (policy-facts).",
          "You want a backing service you can use right away. No actual backing service (which error tracking, monitoring, notification, flag or metering service) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Requests for a flag or a value that is not pinned are not sent.",
          "value_ref is required only for SET, and a READ request carrying a value is not sent. A request whose op and effect do not match (READ is NONE; SET and CLEAR are EXTERNAL_REVERSIBLE) is not sent either.",
          "A receipt whose state does not answer the op (anything other than READ→READ, SET→SET, CLEAR→CLEARED or FAILED) is not accepted.",
          "A flag receipt that claims authorization is not accepted.",
          "It does not send when a reference is empty, contains a path or \"..\" (path traversal), or has additional fields. Only references in the pinned lists are allowed, and an empty list allows nothing.",
          "The same request_id is never sent twice (ALREADY_DONE). A request with the same idempotency key but different content is not sent (REFUSED), and a duplicate (DEDUP_HIT) receipt that claims a new effect is not accepted.",
          "It matches the receipt. A spoofed request_digest, a receipt that increases the external effect, a receipt whose effect is unknown (UNKNOWN) and a receipt for a different request are not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A backing-service failure is returned as UNKNOWN. A resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not derive authorization from a flag's state.",
          "It does not return the flag's value itself (what READ returns). It returns only the state.",
          "It does not include a function for deciding who gets which feature. It only checks feature-flag requests, carries them to the backing service and matches the receipt.",
          "Setting a flag is not approval (approval: false).",
          "Judging authorization, semantic correctness and business facts is the role of the Kernels (monitor-detect and policy-facts) and of people. This Tsugite only checks before sending and matches after receiving.",
          "It does not include an actual backing service. Which service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/observability-ext/v1. Returns: submit(request) on createObservabilityExtClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. DONE carries flag_state and authorization in its result. User provides: cfg with the pinned lists (such as flag_refs and flag_value_refs) and a port (submit, and optionally lookup). Side effect: NONE for READ; EXTERNAL_REVERSIBLE for SET and CLEAR (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "K-08",
        "category": "K/可観測性・運用",
        "kind": "Tsugite",
        "title": "Usage meter — Observability Extension Tsugite",
        "summary": "The Observability Extension Tsugite (継手, tsugite), as K-08 usage meter, checks a request to record a usage event (UsageMeterRequest) against the pinned meters and units and the quantity limit before carrying it to the backing service, and matches the returned receipt against the contract. It does not bill, and it does not check that the quantity is correct. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When usage is recorded to an external metering backing service, it prevents recording with meters or units that have not been decided, sending fractional, negative or over-limit quantities, and misreading a recorded quantity as a verified business fact.",
        "use_when": [
          "You want to carry usage events to a metering backing service.",
          "You want to decide in advance which meters (meter_refs) and units (unit_refs) may be recorded and the quantity limit (max_quantity), and send nothing else.",
          "You do not want the same usage recorded twice. A different quantity under the same idempotency key is not sent, and the same request_id is never sent twice."
        ],
        "do_not_use_when": [
          "You want to bill. This Tsugite carries usage events and does not include a billing function.",
          "You want to use a recorded quantity for billing as is. It does not check that the quantity is correct (whether it can be used for billing), and it is not a verified business fact.",
          "You want to record fractional quantities. Quantities must be integers of 0 or more; a fractional quantity cannot be given a digest and is not sent.",
          "You want a backing service you can use right away. No actual backing service (which error tracking, monitoring, notification, flag or metering service) is included, and it has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Requests for a meter or a unit that is not pinned are not sent.",
          "A quantity that is fractional, negative or over the limit (max_quantity) is not sent. max_quantity is required when recording usage.",
          "A receipt that claims the quantity is a verified business fact is not accepted.",
          "It does not send when a reference is empty, contains a path or \"..\" (path traversal), or has additional fields. Only references in the pinned lists are allowed, and an empty list allows nothing.",
          "The same request_id is never sent twice (ALREADY_DONE). A request with the same idempotency key but different content is not sent (REFUSED), and a duplicate (DEDUP_HIT) receipt that claims a new effect is not accepted.",
          "It matches the receipt. A spoofed request_digest, a receipt that increases the external effect, a receipt whose effect is unknown (UNKNOWN) and a receipt for a different request are not accepted (RECEIPT_REJECTED), and it stops from then on (HALTED).",
          "A backing-service failure is returned as UNKNOWN. A resend is resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not bill. It only checks usage events, carries them to the backing service and matches the receipt.",
          "It does not check that the quantity is correct (whether it can be used for billing). A recorded quantity is not a verified business fact.",
          "It does not handle fractional quantities (the contract allows a number, but this Tsugite narrows it to integers of 0 or more).",
          "Successfully recording usage is not approval (approval: false).",
          "Judging authorization, semantic correctness and business facts is the role of the Kernels (monitor-detect and policy-facts) and of people. This Tsugite only checks before sending and matches after receiving.",
          "It does not include an actual backing service. Which service to connect to, the port implementation and the pinned lists are undecided and will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/observability-ext/v1. Returns: submit(request) on createObservabilityExtClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. DONE carries meter_state and business_fact_verification in its result. User provides: cfg with the pinned lists (such as meter_refs and unit_refs) and max_quantity, and a port (submit, and optionally lookup). Side effect: EXTERNAL_REVERSIBLE (NONE for DEDUP_HIT and FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-01",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "Time zone conversion — Time zone conversion part (Primitive)",
        "summary": "The Time zone conversion part (Primitive) is a standalone part that converts between epoch milliseconds (a safe integer) and the wall-clock time (year, month, day, hour, minute, second and millisecond) in an IANA zone. The zone rules follow Node.js's built-in Intl (ICU's tz data), and every result carries the tz data and ICU versions. It does not silently fix a wall-clock time that does not exist or that occurs twice; it returns { ok, errors } and does not throw (fail-closed).",
        "problem": "Behind \"times are off\", it prevents silently moving a time that does not exist because of a daylight-saving jump, or a time that occurs twice because of an overlap, to some other time, and prevents accepting fields such as 30 February, hour 24 or second 60 by carrying them over. Because results carry the tz data and ICU versions, you can check afterwards which rules produced an answer.",
        "use_when": [
          "You want to turn an instant in epoch milliseconds into the wall-clock time in an IANA zone (for example Asia/Tokyo) and its offset from UTC (toZoned).",
          "You want to turn a wall-clock time in a zone into an instant in epoch milliseconds (fromZoned). A wall-clock time that occurs twice is stopped by default; only when earlier or later is given explicitly does it choose the earlier or the later one.",
          "When a wall-clock time that occurs twice is stopped, you want to receive the candidate instants, earlier and later (candidates_ms), and decide on the caller's side which to use.",
          "You want to keep, together with the result, which tz data and ICU versions produced the answer (tzdata's tz, icu and matches_pin).",
          "You need conversion that does not throw. Neither toZoned nor fromZoned throws an exception, and neither rewrites its input.",
          "You want to call it locally, as processing with no outside boundary that reads neither the current time, random numbers nor the environment's locale. It has no dependency packages."
        ],
        "do_not_use_when": [
          "You need the same answer regardless of the tz data or ICU version. Answers depend on this execution environment's tz data (2026c) and ICU (78.3), and if the versions change, the same input may give a different answer. Other versions have not been checked (tzdata.matches_pin becomes false).",
          "You handle times before 1970 or after 2100. It stops them with OUT_OF_RANGE.",
          "You want to handle leap seconds. Instants are POSIX time, which does not count leap seconds, and second 60 is stopped with INVALID_DATE.",
          "You want to pass a zone written as an offset from UTC, such as +09:00. A zone must have the form of an IANA name; anything else is stopped with INVALID_ZONE.",
          "You want a wall-clock time that does not exist to be moved to a nearby time. It stops with NONEXISTENT_LOCAL_TIME and, whatever disambiguation says, does not move it to another time.",
          "You use it on a version other than Node.js v24.21.0 (tz data 2026c, ICU 78.3). Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_INSTANT when the instant is not a safe integer (a fraction, NaN, Infinity, a string, a Date or a bigint), and with OUT_OF_RANGE when the instant or the year is outside 1970 to 2100.",
          "It stops with INVALID_ZONE when zone is not a string, is longer than 64 characters or does not have the form of an IANA name (an offset notation, whitespace or an empty segment), and with UNKNOWN_ZONE when the form fits but this execution environment's Intl does not know the zone.",
          "It stops with INVALID_LOCAL when the wall-clock time is not a plain object, lacks a required field, has other keys, a getter, a non-enumerable property or a symbol key, or has a value that is not an integer. It does not call getters.",
          "When a field's value is out of range (30 February, 29 February in a common year or in 2100, 31 April, month 13, hour 24, minute 60, second 60, millisecond 1000 or a negative value), it stops with INVALID_DATE without carrying over.",
          "It stops with NONEXISTENT_LOCAL_TIME for a wall-clock time that does not exist because of a daylight-saving jump, and with AMBIGUOUS_LOCAL_TIME for one that occurs twice because of an overlap (when disambiguation is reject, the default), and attaches the candidates.",
          "It stops with INVALID_OPTIONS when options is not a plain object, has other keys or a getter, or disambiguation is not reject, earlier or later, and with INSPECTION_FAILED when inspecting the value itself raises an exception (a Proxy trap and so on).",
          "Even when the wall-clock time is within range, if the converted instant falls outside the range (for example 1970-01-01 08:59 in Asia/Tokyo), it stops with OUT_OF_RANGE."
        ],
        "what_it_does_not_do": [
          "It holds no tz data of its own. The conversion rules are those of Node.js's ICU, and answers are correct only when this execution environment's tz data is correct. Future times are projections under the rules as of when that tz data was made, and rule changes (such as abolishing daylight saving time) are not reflected until a tz data version containing them is released.",
          "Answers were independently cross-checked only for UTC and Asia/Tokyo (1970 to 2100) and for America/New_York and Europe/London (2010 to 2035). Other zones have only round-trip and property checks; their answers have not been separately confirmed.",
          "fromZoned builds candidates from the offsets seen every 3 hours over the 15 hours before and after. In zones and periods where transitions follow one another at intervals shorter than 3 hours, it may miss candidates (whether this happens in the tz data for 1970 to 2100 has not been fully checked).",
          "It does not judge meaning such as which time is the cutoff, which zone to use, or business days. That stays with the caller (the Kernel and the business).",
          "Compatibility of answers with Temporal or outside libraries has not been checked. Proxy traps were checked only for the operations this part inspects (getPrototypeOf, ownKeys and getOwnPropertyDescriptor).",
          "Being able to convert does not mean the time is correct or that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular date library."
        ],
        "integration": "Call toZoned(instantMs, zone) and fromZoned(local, zone, options) in src/timezone.js (they do not throw). Input: epoch milliseconds or a wall-clock time, an IANA zone name, and an optional { disambiguation }. Returns: { ok: true, errors: [], local or instant_ms, offset_seconds, zone, resolved_zone, tzdata, … } or { ok: false, errors: [{ code }], tzdata } (code is from error_taxonomy). Side effect: NONE. There is no outside connection and no credential, and the caller provides nothing. The state is a cache of formatters (only within the process) and does not affect the answers.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-02",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "Machine translation transport (i18n) — Localization Transport Tsugite",
        "summary": "The Localization Transport Tsugite (継手, tsugite), for the machine translation transport (i18n) item, checks a machine-translation request (TranslationRequest) against the pinned backends and locales (source and target), carries it to a translation backend, and matches the returned receipt (TranslationReceipt) against the contract. It carries only references, not the text itself. It does not switch display text and does not manage catalogs. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying requests to a translation backend, it prevents sending the text itself, sending to a backend or locale that is not pinned, and mistaking a machine translation for a human-verified translation or an authoritative text.",
        "use_when": [
          "You want to carry machine-translation requests to a pinned translation backend using only references, not the text itself.",
          "You want to limit the source and target locales to pinned lists and not send requests whose source and target locales are the same.",
          "You want to accept the returned receipt only after matching request_id, request_digest and references. The DONE result carries translation_state, output_ref, human_verification and authoritative_status.",
          "Even if the result becomes unknown (UNKNOWN) because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to switch display text by language, or to manage a multilingual text catalog. This Tsugite carries machine-translation requests and does not include any function for switching text or managing catalogs.",
          "You want to treat machine-translation output as a human-verified translation or an authoritative text. Receipts that claim so are not accepted.",
          "You want to check that a translation is correct, or to obtain approval to publish translated text. Checking translations and approving publication are the role of external judgement and of people and organizations.",
          "You want localization that local computation can handle, such as time conversion, currency or number formatting, unit conversion or sorting. That is not in scope for this Tsugite.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backend you can use right away. No actual translation, address or reference-data backend is included."
        ],
        "what_it_checks": [
          "Requests to a backend or locale that is not pinned are not sent. Requests are also not sent when a configured list is empty or contains an empty entry (an empty list allows nothing).",
          "Requests with an empty reference, a reference containing a path or .., additional fields, or a value different from the fixed value (const) are not sent.",
          "Requests whose source and target locales are the same are not sent.",
          "Receipts that claim a human-verified translation (human verification) or an authoritative text (authoritative) are not accepted.",
          "Receipts that report success (PRODUCED) without an output reference (output_ref), and receipts that report failure (FAILED or UNAVAILABLE) yet claim an output reference or an effect, are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not switch display text and does not manage multilingual catalogs. This Tsugite carries machine-translation requests and does not include those functions.",
          "A machine translation is neither a human-verified translation nor an authoritative text. It does not check whether a translation is correct.",
          "No result is approval (approval: false). Translated or found is not approval to publish, not confirmation of a deliverable address, and not authoritative.",
          "It does not include an actual translation, address or reference-data backend and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific translation, map or calendar service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/localization/v1. Input: pass a TranslationRequest to submit(request) on createLocalizationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (the pinned lists backends, locales, regions and datasets) and a port to the backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_REVERSIBLE (NONE when FAILED or UNAVAILABLE).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-03",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "Currency formatting — Currency formatting part (Primitive)",
        "summary": "The Currency formatting part (Primitive) is a standalone part that turns a safe integer counted in a currency's minor unit (amount_minor) into a string in a fixed notation, using a currency table (7 currencies) and a locale table (ja-JP and en-US) fixed inside the part. It only formats for display; it does not do currency conversion, rounding policy or payment decisions. Values it cannot accept are stopped with fixed codes, and it does not throw (fail-closed).",
        "problem": "Behind the display, it prevents the notation from changing with the internationalization API (ICU) of the environment it runs in, the notation no longer mapping back to the original integer because digits were dropped or rounded, and currencies or locales not in the table, or extra keys (meant as rounding settings and the like), being silently mapped to something close or ignored.",
        "use_when": [
          "You want to turn a safe integer already expressed in a currency's minor unit into a notation string with a symbol and digit grouping, for a currency in the table (the currencies of Japan and the United States, and EUR, GBP, KRW, CNY and BHD) and a locale in the table (ja-JP or en-US) (formatMinor).",
          "You need a notation that does not depend on the environment it runs in. It uses no clock, no random numbers, no environment locale and no internationalization API (ICU); the tables are fixed inside this part's file, and the same input always gives the same text.",
          "You need the notation, when turned back into the minor unit, to return exactly to the absolute value of the integer received (no digits dropped, no rounding).",
          "You want every applicable problem with an input it cannot accept to be returned with the field (field) and a code.",
          "You need formatting that does not throw. formatMinor does not throw, does not rewrite its input and does not call getters."
        ],
        "do_not_use_when": [
          "You want to pass a value with a fraction or a value not yet in the minor unit. This part accepts integers only and does no rounding either. The rounding policy and conversion used when going to the minor unit are the role of the caller or of the G-series parts.",
          "You want to handle currencies or locales other than the 7 currencies and 2 locales in the table. It stops with UNKNOWN_CURRENCY or UNKNOWN_LOCALE and does not map ja-jp, en-GB, ja and the like to a close match.",
          "You need the same output as CLDR or Intl.NumberFormat. It claims no match. How symbols are chosen and written is this part's own rule: the symbol for Japan's currency is the half-width U+00A5 (not the full-width U+FFE5 of CLDR's ja), and BHD is written as its ISO 4217 code followed by a space (U+0020).",
          "You need the symbol placed after the number, other digit grouping (periods, spaces or Indian 2-digit grouping), accounting notation (negative numbers in parentheses), or full-width digits and symbols.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_INPUT when the input is not an object (null, an array, a number, a string and so on), when a key is a getter or setter, or when inspecting the input itself raises an exception (a Proxy trap and so on). It does not call getters.",
          "When the input has keys other than amount_minor, currency and locale (including non-enumerable keys and symbol keys), it stops with UNKNOWN_FIELD instead of silently ignoring them. It does not read inherited keys.",
          "It stops with INVALID_AMOUNT when amount_minor is missing, is not a number (a bigint or a string), is not an integer, is outside the safe integer range, or is NaN or Infinity.",
          "It stops with UNKNOWN_CURRENCY when currency is missing, is not a string, or is not a code in the table (including lowercase, surrounding whitespace, and inherited names such as __proto__).",
          "It stops with UNKNOWN_LOCALE when locale is missing, is not a string, or is not a locale in the table, without mapping it to a close locale.",
          "When several fields cannot be accepted, it lists every applicable problem in errors in the order amount_minor → currency → locale (only one when the input has the wrong shape or has an extra key)."
        ],
        "what_it_does_not_do": [
          "The Currency formatting part only formats for display; it does not include currency conversion, rounding, tax, billing or payment decisions. Those stay with the G-series parts and the caller.",
          "It does not check what the received integer means (the rounding used when it was put into the minor unit, or whether the currency is correct). It writes the received integer as it is.",
          "It does not handle anything other than the 7 currencies and 2 locales in the table. The output for ja-JP and en-US is the same (comma for digit grouping, period for the decimal point, grouping every 3 digits, symbol before the number), and it creates no per-locale differences.",
          "Symbols are the Unicode characters as they are, with no normalization. The tables are not a copy of CLDR or of each country's practice but this part's own rule. The tables are frozen, and the caller cannot rewrite them.",
          "Producing a notation string does not mean that the figure is correct or that billing or payment may go ahead (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "The workshop's definition tsugite-l-03 is only an inspection type; it has no formatting implementation, so there is nothing to compare against. This part is a candidate to become the formatting implementation.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular internationalization library."
        ],
        "integration": "Call formatMinor({ amount_minor, currency, locale }) in src/currency.js (it does not throw). Input: an object with only those three keys; amount_minor is a safe integer in the minor unit, currency is an ISO 4217 code in the table, and locale is ja-JP or en-US. Returns: { ok: true, errors: [], text } or { ok: false, errors: [{ code, field }], text: null } (code is from error_taxonomy). text is, in order, a sign only when negative, the symbol from the table, the integer part grouped every 3 digits, and a fractional part with as many digits as the minor unit has. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-04",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "Unit conversion — Unit conversion part (Primitive)",
        "summary": "The Unit conversion part (Primitive) is a standalone part that converts a value given as a decimal string using exact rational coefficients as defined (a BigInt numerator and denominator), and returns a decimal string rounded half-even to 0 to 18 fractional digits. Length, mass and pressure are proportional, temperature (°C, °F and K) is linear, and no floating point is used. The dB family (dBm↔mW) is not in v1. Values it cannot accept are stopped with fixed codes, and it does not throw (fail-closed).",
        "problem": "It prevents floating-point error from entering midway through a conversion (\"0.1\" no longer being exactly 1/10 and so on), silently computing a conversion between different dimensions or a temperature below absolute zero, and silently mapping similarly spelled units or extra keys (meant as precision settings and the like) to something close or ignoring them.",
        "use_when": [
          "You want to convert a value within the same dimension: length (m, km, cm, mm, in, ft, mi), mass (kg, g, lb, oz), pressure (Pa, kPa, MPa, bar, psi) or temperature (K, C, F) (convert).",
          "You want the result of the conversion as a value rounded half-even only once, from the exact rational number to fraction_digits fractional digits (no rounding midway).",
          "You need the coefficients to come from defined values (in = 0.0254 m, lb = 0.45359237 kg, bar = 100000 Pa, K = °C + 273.15 and so on).",
          "You want to use it as the conversion rule when injecting derived values.",
          "You want to call it locally, as processing with no outside boundary and no state, unaffected by the clock, random numbers or the environment's locale. convert does not throw, does not rewrite its input and does not call getters."
        ],
        "do_not_use_when": [
          "You want to convert the dB family (dBm↔mW, dB and so on). The catalog's one-liner says \"dBm/MPa etc.\", but the dB family is logarithmic and its results are not rational numbers, so it is not in v1 and is stopped with UNKNOWN_UNIT as a unit not in the table.",
          "You want to use units other than the 19 in the table. It does not automatically build prefix combinations such as μm or hPa.",
          "You want to convert temperature differences (Δ°C ↔ Δ°F). Temperatures are converted as absolute temperatures, and values below absolute zero are stopped.",
          "You need significant figures, propagation of error (uncertainty), or rounding other than half-even.",
          "You need locale-dependent notation (a comma as the decimal point and so on) or output with digit grouping. The output is only a decimal string without digit grouping.",
          "You need the same results as other implementations that convert with floating point. Results may differ near rounding boundaries.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_INPUT when the input is not an object, when a key is a getter or setter, or when inspecting the input itself raises an exception (a Proxy trap and so on), and with UNKNOWN_FIELD when it has keys other than the five (including non-enumerable keys and symbol keys).",
          "It stops with MALFORMED_VALUE when value is not a string, is longer than 64 characters, or does not fit the decimal grammar (exponents, hexadecimal, NaN, Infinity, +, whitespace, digit grouping, \"1.\", \".5\", extra leading zeros, or non-ASCII digits).",
          "It stops with UNKNOWN_UNIT when from or to is not a unit in the table (including the dB family such as dBm and mW, similar spellings such as \"°C\", \"KM\" and \"kpa\", and inherited names such as __proto__).",
          "When from and to have different dimensions, it stops with INCOMPATIBLE_UNITS instead of silently computing.",
          "When a temperature value is below absolute zero (0 K = -273.15 °C = -459.67 °F), it stops with BELOW_ABSOLUTE_ZERO. Exactly absolute zero is accepted.",
          "It stops with INVALID_FRACTION_DIGITS when fraction_digits is not an integer from 0 to 18, and with UNSUPPORTED_ROUNDING when rounding is missing or is not exactly \"half-even\".",
          "When several fields cannot be accepted, it lists every applicable problem in errors in the order value → from → to → fraction_digits → rounding."
        ],
        "what_it_does_not_do": [
          "The Unit conversion part does not include conversion of the dB family, including the dBm in the catalog's one-liner. v1 covers only length, mass, pressure (including MPa) and temperature, which can be written exactly as rational numbers.",
          "It does not decide which values may be converted, nor does it reconcile (reconcile). That stays with the caller.",
          "The coefficient values were copied from the definitions, and the oracle was built from the same definitions, so a copying error in a definition itself could enter both in the same way. What was checked is limited to known-answer tests (1 in = 25.4 mm, 1 lb = 453.59237 g, 1 psi = 6894.757293168361 Pa and so on).",
          "Unit names are the ASCII symbols as they are (\"C\" is Celsius, not the coulomb).",
          "Being able to convert does not mean the original value or the choice of unit is correct, or that design or decisions may be based on the value (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "The workshop's definition tsugite-l-04 is only an inspection type; it has no conversion implementation, so there is nothing to compare against.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular unit conversion library."
        ],
        "integration": "Call convert({ value, from, to, fraction_digits, rounding }) in src/unitconvert.js (it does not throw). Input: an object with only those five keys; value is a decimal string of up to 64 characters, from and to are unit names in the table, fraction_digits is an integer from 0 to 18, and rounding is exactly \"half-even\" (it cannot be omitted). Returns: { ok: true, errors: [], value } or { ok: false, errors: [{ code, field }], value: null } (code is from error_taxonomy). Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-05",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "Address candidate lookup — Localization Transport Tsugite",
        "summary": "The Localization Transport Tsugite (継手, tsugite), for the address candidate lookup item, checks an address candidate lookup request (AddressLookupRequest) against the pinned backends and regions, carries it to an address backend, and matches the returned receipt (AddressLookupReceipt) against the contract. It carries only references, not the address text. It does not verify that an address is deliverable and does not prove residence. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying lookups to an address backend, it prevents sending the address text itself, sending to a backend or region that is not pinned, and mistaking a found address candidate for confirmation of a deliverable address or proof of residence.",
        "use_when": [
          "You want to carry address candidate lookups to a pinned address backend using only a reference (query_ref), not the address text.",
          "You want to limit the regions looked up to a pinned list.",
          "You want to carry the lookup as one with no external effect (side effect NONE).",
          "You want to accept the returned receipt only after matching request_id, request_digest and references. The DONE result carries lookup_state, candidate_ref, deliverability and residence_proof.",
          "Even if the result becomes unknown (UNKNOWN) because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to verify whether an address is deliverable (whether delivery is possible). An address candidate is not confirmation of a deliverable address, and receipts that claim so are not accepted.",
          "You want proof that a person lives at an address. An address candidate is not proof of residence. Proving a person's address is the role of external judgement and of people and organizations.",
          "You want the mechanism that unifies variant address notations (normalization) itself. This Tsugite carries address candidate lookup requests and does not include any normalization function.",
          "You want to send the address text to the backend. Only references are carried, and a reference containing whitespace is treated as address text and not sent.",
          "You want localization that local computation can handle, such as phone number normalization. That is not in scope for this Tsugite.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backend you can use right away. No actual translation, address or reference-data backend is included."
        ],
        "what_it_checks": [
          "Requests to a backend or region that is not pinned are not sent. Requests are also not sent when a configured list is empty or contains an empty entry (an empty list allows nothing).",
          "If the lookup reference (query_ref) contains whitespace, it is treated as address text and not sent.",
          "Requests with an empty reference, a reference containing a path or .., additional fields, or a value different from the fixed value (const) are not sent.",
          "Receipts that claim confirmation of a deliverable address (deliverability) or proof of residence (residence proof) are not accepted.",
          "Receipts that report not found (NOT_FOUND) yet carry a candidate reference (candidate_ref), and receipts that report failure yet claim an output reference or an effect, are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not verify that an address is deliverable and does not prove residence. An address candidate is neither of these. Confirming a deliverable address and proving a person's address are the role of external judgement and of people and organizations.",
          "It does not include any address normalization function (unifying variant notations). This Tsugite carries address candidate lookup requests.",
          "It does not check whether an address candidate is correct. Detection of address text is also only a net that looks for whitespace and does not recognize every address text; the main protection is the design of carrying only references.",
          "No result is approval (approval: false). Translated or found is not approval to publish, not confirmation of a deliverable address, and not authoritative.",
          "It does not include an actual translation, address or reference-data backend and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific translation, map or calendar service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/localization/v1. Input: pass a AddressLookupRequest to submit(request) on createLocalizationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (the pinned lists backends, locales, regions and datasets) and a port to the backend (submit, and optionally lookup). Secrets are held by the port. Side effect: NONE (an address lookup).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-06",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "Phone number normalization (E.164) — Phone number normalization (E.164) part (Primitive)",
        "summary": "The Phone number normalization (E.164) part (Primitive) is a standalone part that turns a phone number string into E.164 (\"+\", the country code, then the national significant number, NSN). The only table it uses is the small versioned table this part holds (erabazu-e164-metadata/2026-09-30), which covers three regions: JP (+81), NANP (US/CA, +1) and GB (+44). Values it cannot accept are stopped with fixed codes (fail-closed). Being able to normalize is not proof that the number exists or can be reached.",
        "problem": "Behind SMS, it brings variations in how recipient numbers are written (spaces, hyphens, dots, parentheses, national and international forms) into a fixed form. In doing so, it does not guess a country code or region not in the table, and does not silently drop an extension or another international prefix; it stops with a fixed code.",
        "use_when": [
          "You want to receive a JP, NANP (US/CA) or GB phone number string as an E.164 string, split into the table's region, country code and NSN (normalize).",
          "You want to turn a number in national form (not starting with +) into E.164 with the national prefix (trunk) removed, passing default_region (JP, US, CA or GB).",
          "You want to produce a string that passes the SMS recipient format check. This part's output always matches the notification Tsugite's SMS recipient format check (/^\\+[1-9][0-9]{6,14}$/) (this was cross-checked once in the workspace and is not included in the part's tests).",
          "You want to keep, together with the result, which table version it was normalized with. Every result carries metadata_revision.",
          "You need a way of calling it that does not throw. normalize does not throw, does not rewrite its input (including options) and does not call getters on options."
        ],
        "do_not_use_when": [
          "You want to confirm that a number exists, is in use, can be reached or belongs to someone. This part does not confirm that (existence_verified is always false). Confirming it needs a separate means (an SMS confirmation code and so on).",
          "You want to check against the whole numbering plan (allocated number ranges, the list of area codes, number types). It also accepts numbers that merely match the table's lengths and leading digits.",
          "You handle regions other than JP, NANP and GB, or you want to tell US and CA apart. region is the table's heading (NANP), not the country where the line is.",
          "You want numbers with extensions, numbers starting with another international prefix (00, 010, 011), full-width digits, or mnemonic letters to be converted. v1 does not accept them.",
          "You want changes to numbering plans to be tracked automatically. The table's values are conservative ones made on 2026-09-30.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with NOT_A_STRING when text is not a string, and with INPUT_TOO_LONG, without reading the content, when it is longer than 64 characters.",
          "It stops with INVALID_OPTIONS when opts is not a plain object, has keys other than default_region or a getter, or inspecting it itself raises an exception (a Proxy trap and so on), and with UNKNOWN_REGION when default_region is not JP, US, CA or GB.",
          "It stops with EXTENSION_NOT_SUPPORTED when there is an extension marker (#, ; or a comma, or x, ext or extension), and with LETTERS_NOT_ALLOWED when there are ASCII letters, instead of silently dropping them.",
          "It stops with INVALID_CHARACTER when there are characters other than those it accepts (full-width digits, tabs, line breaks, no-break spaces, a + that is not at the start, a second + and so on), and with EMPTY_NUMBER when there are no digits at all.",
          "It stops with UNSUPPORTED_COUNTRY_CODE when, in international form, the country code is not 1, 44 or 81, and with MISSING_REGION when the number is in national form but default_region is missing; it does not guess.",
          "It stops with INTERNATIONAL_PREFIX_NOT_SUPPORTED when a national-form number starts with that region's international prefix (010 or 00 for JP, 00 for GB, 011 for NANP), and with MISSING_TRUNK_PREFIX when a JP or GB national-form number does not start with 0.",
          "It stops with INVALID_LENGTH when the number of NSN digits is not a length in the table, and with INVALID_NUMBER_PATTERN when a digit in a fixed position is below the lower bound (a trunk 0 after the country code, or a leading 0 or 1 in a NANP area code or exchange code).",
          "When it stops, there is always exactly one error, and the order of checks is fixed (the order of error_taxonomy)."
        ],
        "what_it_does_not_do": [
          "The Phone number normalization (E.164) part only puts the recipient string into a fixed form. It does not include sending SMS or placing calls; that is on the side of the notification Tsugite (D).",
          "Being able to normalize is not proof that the number exists, can be reached or belongs to someone, or that that person may be contacted (existence_verified is always false).",
          "It only looks at ASCII characters. It does not convert full-width digits and the like to half-width or do Unicode normalization, and it does not check that parentheses match or where decoration is placed.",
          "The table is a conservative one limited to 3 regions. Adding a region is separate work: raising the table's version, adding tests and oracles, and re-verifying.",
          "Being able to normalize does not mean the number may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a copy of, or a template for how to use, a particular phone number library (libphonenumber and so on)."
        ],
        "integration": "Call normalize(text, opts) in src/phone.js (it does not throw). Input: a string text of up to 64 characters and opts, which is omitted or only { default_region } (required for the national form). Returns: { ok: true, errors: [], e164, region, country_code, national_number, metadata_revision, existence_verified: false } or { ok: false, errors: [{ code }], e164: null, … } (code is from error_taxonomy). toE164 throws PhoneError when it cannot accept a value. Side effect: NONE. There is no state, no outside connection and no credential, and the caller provides nothing.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-07",
        "category": "L/地域化・整形",
        "kind": "Tsugite",
        "title": "Calendar reference data — Localization Transport Tsugite",
        "summary": "The Localization Transport Tsugite (継手, tsugite), for the calendar reference data item, checks a reference-data request (ReferenceDataRequest), such as for a holiday calendar, against the pinned backends, datasets and locales, carries it to a reference-data backend, and matches the returned receipt (ReferenceDataReceipt) against the contract. It only fetches reference data and does not perform date calculations such as Japanese era dates or business days. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying requests to a reference-data backend, it prevents sending to a backend, dataset or locale that is not pinned, and mistaking fetched reference data for an authoritative calendar or a jurisdictional authority.",
        "use_when": [
          "You want to fetch reference data, such as a holiday calendar, from a pinned backend and dataset.",
          "You want to limit the locale of the reference data (locale_ref) to a pinned list.",
          "You want to carry the fetch as one with no external effect (side effect NONE).",
          "You want to accept the returned receipt only after matching request_id, request_digest and references. The DONE result carries data_state, data_ref, authoritative_status and jurisdiction_authority.",
          "Even if the result becomes unknown (UNKNOWN) because of a backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want date calculations, such as conversion to Japanese era dates or business-day calculation. This Tsugite carries requests that fetch reference data and does not include any date calculation function.",
          "You want to treat fetched reference data as an authoritative calendar or a jurisdictional authority. Receipts that claim so are not accepted.",
          "You want localization that local computation can handle, such as time conversion. That is not in scope for this Tsugite.",
          "You want to check that reference data is correct. Judging the authoritative calendar is the role of external judgement and of people and organizations.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a backend you can use right away. No actual translation, address or reference-data backend is included."
        ],
        "what_it_checks": [
          "Requests to a backend, locale or dataset that is not pinned are not sent. Requests are also not sent when a configured list is empty or contains an empty entry (an empty list allows nothing).",
          "Requests with an empty reference, a reference containing a path or .., additional fields, or a value different from the fixed value (const) are not sent.",
          "Receipts that claim authoritative reference data (authoritative) or a jurisdictional authority (jurisdiction authority) are not accepted.",
          "Receipts that report failure (FAILED, UNAVAILABLE or NOT_FOUND) yet claim an output reference (data_ref) or an effect are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that increase the external effect beyond the intent and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not perform date calculations (Japanese era dates or business days). This Tsugite carries requests that fetch reference data and does not include any date calculation function.",
          "Reference data is neither an authoritative calendar nor a jurisdictional authority. ERABAZU does not claim jurisdictional authority. Judging the authoritative calendar is the role of external judgement and of people and organizations.",
          "It does not check whether reference data is correct.",
          "No result is approval (approval: false). Translated or found is not approval to publish, not confirmation of a deliverable address, and not authoritative.",
          "It does not include an actual translation, address or reference-data backend and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific translation, map or calendar service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/localization/v1. Input: pass a ReferenceDataRequest to submit(request) on createLocalizationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (the pinned lists backends, locales, regions and datasets) and a port to the backend (submit, and optionally lookup). Secrets are held by the port. Side effect: NONE (reference data).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "L-08",
        "category": "L/地域化・整形",
        "kind": "Primitive",
        "title": "Collation order (collation) — Collation order part (Primitive)",
        "summary": "The Collation order part (Primitive) is a standalone part that compares and sorts strings by a language's collation order. It leaves the collation itself to Node.js's built-in Intl.Collator (ICU); this part fixes the locale (ja or en) and the strength (base, accent or variant) by an allow-list, and stops if they are not resolved exactly as requested. Being equal under collation (sameKey) is not being identical. Values it cannot accept are stopped with fixed codes (fail-closed).",
        "problem": "Behind lists, it prevents the sort order from silently falling back to the execution environment's default locale and changing, strings that are equal under collation from being treated as identical, and the order among strings that are equal under collation from being left undetermined. Because results carry the ICU version, you can check afterwards which version's order it is.",
        "use_when": [
          "You want to sort an array of strings by the ja or en collation order and receive the result and the ICU version (sortList; it does not throw).",
          "You want strings that are equal under collation to stay in their original order (sortStable), or you want ties decided by UTF-16 code unit order to make a total order (sortTotal).",
          "You want to compare two strings and receive one of -1, 0 or 1 (compare; it is antisymmetric and never returns -0).",
          "You want to sort only by an explicitly given locale, independent of the execution environment's default locale. If the Collator is not resolved exactly as requested, it stops.",
          "You want to sort without rewriting the input. It returns a new array even if the input is already sorted, and does not call getters on elements."
        ],
        "do_not_use_when": [
          "You need the same order regardless of the ICU version. The order depends on the execution environment's ICU (78.3), and if the version changes, the same input may give a different order. Other versions have not been checked (icu.matches_pin becomes false).",
          "You want to use it for duplicate detection or as keys. Being equal under collation is not being identical (for example, hiragana A U+3042 and katakana A U+30A2 under ja with variant).",
          "You want to sort kanji by reading (on or kun readings). The order of kanji in ja follows ICU's ja rules (JIS order). To sort by reading, the caller passes the readings and sorts by them.",
          "You want to use locales other than ja and en (including ja-JP, EN and und). It stops with UNSUPPORTED_LOCALE.",
          "You need numeric collation (comparing \"10\" and \"2\" as numbers). This part does not do that.",
          "You use it on a version other than Node.js v24.21.0 (ICU 78.3). Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_OPTIONS when opts is not a plain object, when its keys are not exactly locale and strength (plus order for sortList), or when order is neither stable nor total.",
          "It stops with UNSUPPORTED_LOCALE when locale is neither ja nor en, and with UNSUPPORTED_STRENGTH when strength is not base, accent or variant.",
          "When Intl.Collator is not resolved exactly to the requested locale and the fixed settings (for example because ICU lacks that locale), it stops with LOCALE_NOT_AVAILABLE instead of silently falling back to another locale.",
          "It stops with NOT_A_STRING when a value or element being compared is not a string (including String objects, holes and getter elements), and with STRING_TOO_LONG when it exceeds 65,536 code units; for an element it returns the position in index.",
          "It stops with NOT_AN_ARRAY when list is not an array (including array-like objects and Sets), and with LIST_TOO_LONG when it exceeds 100,000 elements.",
          "When inspecting a value itself raises an exception in sortList (a Proxy trap and so on), it returns INSPECTION_FAILED and does not throw."
        ],
        "what_it_does_not_do": [
          "It holds no collation order of its own. The collation order is ICU's (78.3) and has not been independently rebuilt. The fuzz oracle also uses the same ICU Collator, so whether ICU's order is correct has not been checked (what was checked is the sorting properties, stability, tie-breaking, the codes it stops with, and a few known answers for ja and en).",
          "The sort order is not necessarily what users expect (dictionary, phone book or reading order and so on). The judgement of which order to show stays with the caller.",
          "sortTotal's code-unit comparison does no Unicode normalization.",
          "Only sortList turns Proxy traps into INSPECTION_FAILED. The throwing functions (compare, sameKey, sortStable and sortTotal) let the trap's exception propagate as it is.",
          "Being sorted does not mean the order is correct or that it may be accepted (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular sorting library."
        ],
        "integration": "Call sortList(list, { locale, strength, order }) in src/collation.js (it does not throw). Input: an array of up to 100,000 strings and a plain object with only those three keys. Returns: { ok: true, errors: [], sorted, icu, … } or { ok: false, errors: [{ code, index? }], icu } (code is from error_taxonomy). compare, sameKey, sortStable and sortTotal throw CollationError on a value they cannot accept. Side effect: NONE. There is no outside connection and no credential, and the caller provides nothing. The state is a cache of Collators (only within the process) and does not affect the answers.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-01",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Video call control (WebRTC/SFU) — Two-way Communication Transport Tsugite",
        "summary": "The Two-way Communication Transport Tsugite (継手, tsugite), for the video call control (WebRTC/SFU) item, checks a call signal request (CallSignalRequest) for initiate, answer or end, carries it to a communication backend, and matches the returned receipt (CallSignalReceipt) against the contract. It only carries call signals: it does not carry the video and audio themselves (WebRTC or SFU media). If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying call signals to a communication backend, it prevents placing an email address or phone number in a participant reference, accepting a call state that does not match the signal's op, and mistaking a connection for the participants' consent or identity verification.",
        "use_when": [
          "You want to carry the initiate (INITIATE), answer (ANSWER) and end (END) signals of a video call to a communication backend.",
          "You want to verify that the returned call state corresponds to the signal's op (INITIATE → RINGING or CONNECTED, ANSWER → CONNECTED, END → ENDED; FAILED is allowed for each).",
          "You want to accept the returned receipt only after matching request_id, request_digest, references and op. The DONE result carries op, call_state, consent and identity_verification.",
          "Even if the result becomes unknown (UNKNOWN) because of a communication backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to carry the video and audio themselves (WebRTC or SFU media). This Tsugite carries call signals and does not include any function for carrying video or audio.",
          "You want to check the quality, latency or encryption of the video and audio. That is outside the scope of this part and has not been checked.",
          "You want to treat a connection as the participants' consent or as identity verification. Receipts that claim so are not accepted.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a communication backend you can use right away. No actual communication backend is included."
        ],
        "what_it_checks": [
          "Requests with an empty reference, a reference containing a path or .., additional fields, a value different from the fixed value (const), or an op not in the contract are not sent.",
          "If a participant or subject reference has the form of an email address or phone number, the request is not sent (detection covers only the typical forms).",
          "Receipts whose call state does not match the signal's op are not accepted.",
          "Receipts that claim consent or identity verification are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that claim an effect despite a failure (FAILED), receipts that increase the external effect beyond the intent, and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not carry the video and audio themselves (WebRTC or SFU media). For the video call item, it carries only call signals (initiate, answer and end).",
          "The quality, latency and encryption of the video and audio are outside the scope of this part and have not been checked.",
          "Connected is neither the participants' consent nor identity verification. Identity verification and consent are the role of external judgement and of people and organizations.",
          "No result is approval (approval: false). Delivered, read or connected is not identity verification, consent or authority.",
          "It does not include an actual communication backend (WebRTC, SFU, SIP, or presence or messaging services) and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific call or chat service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/communication/v1. Input: pass a CallSignalRequest (op is INITIATE, ANSWER or END) to submit(request) on createCommunicationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (channel_refs and presence_subjects) and a port to the communication backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_REVERSIBLE (NONE when FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-02",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Voice call control (SIP) — Two-way Communication Transport Tsugite",
        "summary": "The Two-way Communication Transport Tsugite (継手, tsugite), for the voice call control (SIP) item, checks a call signal request (CallSignalRequest) for initiate, answer or end, carries it to a communication backend, and matches the returned receipt (CallSignalReceipt) against the contract. It only carries call signals: it does not carry the audio itself (SIP media). If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying call signals to a communication backend, it prevents placing an email address or phone number in a participant reference, accepting a call state that does not match the signal's op, and mistaking a connection for the participants' consent or identity verification.",
        "use_when": [
          "You want to carry the initiate (INITIATE), answer (ANSWER) and end (END) signals of a voice call to a communication backend.",
          "You want to verify that the returned call state corresponds to the signal's op (INITIATE → RINGING or CONNECTED, ANSWER → CONNECTED, END → ENDED; FAILED is allowed for each).",
          "You want to accept the returned receipt only after matching request_id, request_digest, references and op. The DONE result carries op, call_state, consent and identity_verification.",
          "Even if the result becomes unknown (UNKNOWN) because of a communication backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to carry the audio itself (SIP media). This Tsugite carries call signals and does not include any function for carrying audio.",
          "You want to check the quality, latency or encryption of the audio. That is outside the scope of this part and has not been checked.",
          "You want to treat a connection as the participants' consent or as identity verification. Receipts that claim so are not accepted.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a communication backend you can use right away. No actual communication backend is included."
        ],
        "what_it_checks": [
          "Requests with an empty reference, a reference containing a path or .., additional fields, a value different from the fixed value (const), or an op not in the contract are not sent.",
          "If a participant or subject reference has the form of an email address or phone number, the request is not sent (detection covers only the typical forms).",
          "Receipts whose call state does not match the signal's op are not accepted.",
          "Receipts that claim consent or identity verification are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that claim an effect despite a failure (FAILED), receipts that increase the external effect beyond the intent, and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not carry the audio itself (SIP media). For the voice call item, it carries only call signals (initiate, answer and end).",
          "The quality, latency and encryption of the audio are outside the scope of this part and have not been checked.",
          "Connected is neither the participants' consent nor identity verification. Identity verification and consent are the role of external judgement and of people and organizations.",
          "No result is approval (approval: false). Delivered, read or connected is not identity verification, consent or authority.",
          "It does not include an actual communication backend (WebRTC, SFU, SIP, or presence or messaging services) and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific call or chat service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/communication/v1. Input: pass a CallSignalRequest (op is INITIATE, ANSWER or END) to submit(request) on createCommunicationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (channel_refs and presence_subjects) and a port to the communication backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_REVERSIBLE (NONE when FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-03",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Presence (carries publishing) — Two-way Communication Transport Tsugite",
        "summary": "The Two-way Communication Transport Tsugite (継手, tsugite), for the presence item, checks a presence publish request (PresencePublishRequest) against the pinned subjects (references to the people whose presence may be published), carries it to a communication backend, and matches the returned receipt (PresencePublishReceipt) against the contract. Publishing \"online\" does not mean a person is there. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When publishing presence to a communication backend, it prevents publishing the presence of a person not designated as publishable, placing an email address or phone number in a subject reference, and mistaking \"online\" for a person being there.",
        "use_when": [
          "You want to carry presence (availability and status) publishing to a communication backend, limited to pinned subject references (pseudonyms).",
          "You want to accept the returned receipt only after matching request_id, request_digest and references. The DONE result carries publish_state and human_availability.",
          "You want to treat presence publishing as a reversible external effect (EXTERNAL_REVERSIBLE).",
          "Even if the result becomes unknown (UNKNOWN) because of a communication backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to use \"online\" as confirmation that a person is there. Receipts that claim so are not accepted.",
          "You want to publish the presence of a person not designated as publishable. The presence of subjects that are not pinned is not published.",
          "You want to use an email address or phone number as is in a subject reference. Such requests are not sent.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a communication backend you can use right away. No actual communication backend is included."
        ],
        "what_it_checks": [
          "The presence of subjects that are not pinned is not published. Requests are also not sent when a configured list is empty or contains an empty entry (an empty list allows nothing).",
          "Requests with an empty reference, a reference containing a path or .., additional fields, a value different from the fixed value (const), or an op not in the contract are not sent.",
          "If a subject reference has the form of an email address or phone number, the request is not sent (detection covers only the typical forms).",
          "Receipts that claim a person is there (human availability) are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that claim an effect despite a failure (FAILED), receipts that increase the external effect beyond the intent, and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "Publishing \"online\" does not mean a person is there. It does not check whether a person is there.",
          "It does not decide whose presence may be published. Which subjects are published is decided by the user through the pinned list (presence_subjects).",
          "No result is approval (approval: false). Delivered, read or connected is not identity verification, consent or authority.",
          "It does not include an actual communication backend (WebRTC, SFU, SIP, or presence or messaging services) and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific call or chat service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/communication/v1. Input: pass a PresencePublishRequest to submit(request) on createCommunicationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (channel_refs and presence_subjects) and a port to the communication backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_REVERSIBLE (NONE when FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-04",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Live caption transmission (carries caption segments as digests) — Two-way Communication Transport Tsugite",
        "summary": "The Two-way Communication Transport Tsugite (継手, tsugite), for the live caption transmission item, checks caption segments as real-time message requests (RealtimeMessageRequest) against the pinned channels, carries them to a communication backend, and matches the returned receipt (RealtimeMessageReceipt) against the contract. It carries only the digest, not the caption text. It does not check whether captions are correct. If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying caption segments to a communication backend, it prevents sending the text placed in the digest field, sending to a channel that is not pinned, and mistaking delivered or read for identity verification, comprehension or authority.",
        "use_when": [
          "You want to carry live caption segments to a pinned channel as a digest (payload_digest), not as the text.",
          "You want to accept the returned receipt only after matching request_id, request_digest, references and op. The DONE result carries delivery_state, delivered, identity_verification, comprehension and authority_assertion.",
          "You want to carry messages without placing the form of an email address or phone number in a recipient reference.",
          "Even if the result becomes unknown (UNKNOWN) because of a communication backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to carry the caption text itself. This Tsugite carries caption segments as digests and does not carry the text. Requests that place the text in payload_digest are not sent.",
          "You want to check whether captions are correct. It does not check caption correctness; that is the role of external judgement and of people and organizations.",
          "You want to check the quality, latency or encryption of the captions. That is outside the scope of this part and has not been checked.",
          "You want to treat delivered or read as identity verification, comprehension or authority. Receipts that claim so are not accepted.",
          "You want to retract a delivered message. The side effect of a message is an irreversible external effect (EXTERNAL_IRREVERSIBLE).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a communication backend you can use right away. No actual communication backend is included."
        ],
        "what_it_checks": [
          "Requests to a channel that is not pinned are not sent. Requests are also not sent when a configured list is empty or contains an empty entry (an empty list allows nothing).",
          "Requests that place the text in payload_digest, and requests with a malformed digest, are not sent.",
          "Requests with an empty reference, a reference containing a path or .., additional fields, a value different from the fixed value (const), or an op not in the contract are not sent.",
          "If a recipient reference has the form of an email address or phone number, the request is not sent (detection covers only the typical forms).",
          "Receipts that claim identity verification, comprehension or authority are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that claim an effect despite a failure (FAILED), receipts that increase the external effect beyond the intent, and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not carry the caption text. It carries only the digest of each caption segment. Carrying the caption body, and its quality, latency and encryption, are outside the scope of this part and have not been checked.",
          "It does not check whether captions are correct or what a delivered message contains.",
          "Delivered or read is not identity verification, comprehension, consent or authority.",
          "No result is approval (approval: false). Delivered, read or connected is not identity verification, consent or authority.",
          "It does not include an actual communication backend (WebRTC, SFU, SIP, or presence or messaging services) and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific call or chat service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/communication/v1. Input: pass a RealtimeMessageRequest to submit(request) on createCommunicationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (channel_refs and presence_subjects) and a port to the communication backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_IRREVERSIBLE (NONE when FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-05",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Large and resumable transfer — Large and Resumable Transfer Tsugite",
        "summary": "The Large and Resumable Transfer Tsugite (継手, tsugite) sends large content to a storage destination as one object: it starts a transfer, sends chunks in order from committed_offset, and, if the transfer stops partway, queries the resume position and continues. It finalizes only when all chunks are present and the sha256 of the content equals the payload_digest declared at the start. It does not treat the receipt of chunks or finalization as acceptance of the content (fail-closed).",
        "problem": "When a huge file is sent in chunks, this Tsugite prevents stacking a chunk twice whose result became unknown after a failure partway, and treating a transfer with missing or different content as \"it was sent\". It checks the length and sha256 of each chunk itself before sending, and builds up the overall sha256 only from chunks that were received.",
        "use_when": [
          "You want to send a file too large to send in one go to a storage destination in chunks.",
          "When a transfer stops partway, you want to query the resume position (committed_offset) and send from where it left off.",
          "You want to finalize only after confirming that the sha256 of the complete content equals the payload_digest given at the start.",
          "You want to resolve a chunk whose result is unknown without stacking it twice. It is resolved by a resume position query or by resending the same request (which can also be resolved with lookup).",
          "You want the transfer to stop until a person checks it when a receipt with a conflicting state appears."
        ],
        "do_not_use_when": [
          "You want to send chunks in parallel or receive them out of order. That is outside the scope of contract v1, and this Tsugite does not send them.",
          "You only need storage for sizes that can be sent in one go. That is the Object Store Tsugite (C-01).",
          "You want to treat finalization as a decision that the content is correct or may be accepted. Finalization is not acceptance of the content (content_acceptance is NOT_DERIVED).",
          "You want to resume from another process or across a restart. The transfer state exists only inside this Tsugite and is lost on restart.",
          "You want a storage destination you can use right away. It has never been connected to an actual storage backend, and the number of actual transfers is 0.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the order of chunks. A chunk that is not in order from committed_offset (a skipped offset, an offset that goes back, or a chunk past the total size), or a chunk other than the last that is not chunk_size, is not sent, and nothing is sent to the backend.",
          "It checks the content of chunks. A chunk whose content length or sha256 differs from what was declared is not sent. It checks this itself before sending.",
          "It checks finalization. A finalization before all chunks are present, and a finalization with a sha256 different from the payload_digest given at the start, are not sent. COMPLETED is not accepted without MATCH evidence and a stored_digest.",
          "It checks the configured limits. A content-type that is not allowed, a total size or chunk size over the limit, and WORM (a finalization that cannot be undone) when allow_worm is not true are not sent.",
          "It checks the transfer state. Another send while a chunk is still unresolved, and a send to a transfer that has expired, been closed, been finalized or been aborted, are not sent. A transfer cannot be resumed after it is aborted.",
          "It matches the receipt. A receipt with a conflicting state, a receipt that increases the external effect, a receipt that claims the request_digest of another request, and a completion that claims acceptance are not accepted, and the client stops on a receipt it cannot accept (HALTED).",
          "It checks backend failures. A chunk whose result became unknown due to a failure is treated as UNKNOWN, and if the answer to the resume position query does not match, it is not accepted. An effect that leaves it unknown what remains is not accepted."
        ],
        "what_it_does_not_do": [
          "It does not store the huge file itself. This Tsugite only carries chunks to the storage backend and matches the returned receipts. It has never been connected to an actual storage backend, and the number of actual transfers is 0.",
          "It does not split in parallel or receive out of order. That is outside the scope of contract v1; chunks are sent only in order from committed_offset.",
          "It does not inspect the content itself. Decisions on confirming, accepting and retaining content are the role of external decisions and of people and organizations. Finalization is not acceptance of the content (content_acceptance is NOT_DERIVED).",
          "It does not persist the transfer state. The state exists only inside this Tsugite and is lost on restart. It does not resume from another process either.",
          "It checks only the form of the expiry (expires_at). Expiry is learned from the backend's answer.",
          "No result is approval (approval: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use the multipart upload of a specific cloud storage service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/object-store-resumable/v1. Input: pass a TransferStart, ChunkSend, ResumeQuery, TransferComplete or TransferAbort to submit(request, bytes) on createResumableClient({ cfg, port }); bytes is only for ChunkSend. Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: a port to the backend (submit, and optionally lookup). Side effect: EXTERNAL_IRREVERSIBLE for a WORM finalization and for an abort.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "M-06",
        "category": "M/通信路",
        "kind": "Tsugite",
        "title": "Screen sharing call control — Two-way Communication Transport Tsugite",
        "summary": "The Two-way Communication Transport Tsugite (継手, tsugite), for the screen sharing call control item, checks a call signal request (CallSignalRequest) for initiate, answer or end, carries it to a communication backend, and matches the returned receipt (CallSignalReceipt) against the contract. It only carries call signals: it does not carry the screen itself (screen-sharing media). If any check fails, it does not send and does not accept (fail-closed).",
        "problem": "When carrying call signals to a communication backend, it prevents placing an email address or phone number in a participant reference, accepting a call state that does not match the signal's op, and mistaking a connection for the participants' consent or identity verification.",
        "use_when": [
          "You want to carry the initiate (INITIATE), answer (ANSWER) and end (END) signals of a screen sharing to a communication backend.",
          "You want to verify that the returned call state corresponds to the signal's op (INITIATE → RINGING or CONNECTED, ANSWER → CONNECTED, END → ENDED; FAILED is allowed for each).",
          "You want to accept the returned receipt only after matching request_id, request_digest, references and op. The DONE result carries op, call_state, consent and identity_verification.",
          "Even if the result becomes unknown (UNKNOWN) because of a communication backend failure, you want to resolve it with a lookup instead of sending the same request_id twice."
        ],
        "do_not_use_when": [
          "You want to carry the screen itself (screen-sharing media). This Tsugite carries call signals and does not include any function for carrying the screen.",
          "You want to check the quality, latency or encryption of the screen. That is outside the scope of this part and has not been checked.",
          "You want to treat a connection as the participants' consent or as identity verification. Receipts that claim so are not accepted.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a communication backend you can use right away. No actual communication backend is included."
        ],
        "what_it_checks": [
          "Requests with an empty reference, a reference containing a path or .., additional fields, a value different from the fixed value (const), or an op not in the contract are not sent.",
          "If a participant or subject reference has the form of an email address or phone number, the request is not sent (detection covers only the typical forms).",
          "Receipts whose call state does not match the signal's op are not accepted.",
          "Receipts that claim consent or identity verification are not accepted.",
          "Requests whose side effect differs from the intent are not sent. Receipts that claim an effect despite a failure (FAILED), receipts that increase the external effect beyond the intent, and receipts whose effect is unknown (UNKNOWN) are not accepted.",
          "A receipt for a different request (a spoofed request_digest) is not accepted, and when an unacceptable receipt arrives, it stops (HALTED)."
        ],
        "what_it_does_not_do": [
          "It does not carry the screen itself (screen-sharing media). For the screen sharing item, it carries only call signals (initiate, answer and end).",
          "The quality, latency and encryption of the screen are outside the scope of this part and have not been checked.",
          "Connected is neither the participants' consent nor identity verification. Identity verification and consent are the role of external judgement and of people and organizations.",
          "No result is approval (approval: false). Delivered, read or connected is not identity verification, consent or authority.",
          "It does not include an actual communication backend (WebRTC, SFU, SIP, or presence or messaging services) and has never connected to one. The port implementation and the pinned lists will be settled at a separate gate when connecting.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific call or chat service."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/communication/v1. Input: pass a CallSignalRequest (op is INITIATE, ANSWER or END) to submit(request) on createCommunicationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED. Every result has approval: false. User provides: cfg (channel_refs and presence_subjects) and a port to the communication backend (submit, and optionally lookup). Secrets are held by the port. Side effect: EXTERNAL_REVERSIBLE (NONE when FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-01",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Service discovery and registration — Service Discovery & Registration Tsugite",
        "summary": "The Service Discovery & Registration Tsugite (継手, tsugite) checks requests (RegistryRequest) to an external registry backing service (registry or control plane), namely registering a service instance (REGISTER, with a lease), cancelling a registration (DEREGISTER), extending a lease (HEARTBEAT) and querying (QUERY), carries them, and matches the returned receipt (RegistryReceipt) against the contract. A query result is a timestamped observation; it does not treat a listed endpoint as correct, healthy, permitted or genuine (fail-closed).",
        "problem": "When you rely on an external registry backing service to locate a counterpart, it prevents treating an expired lease as registered, returning an old instance as healthy, and mistaking being listed for correctness, permission or identity. Lease expiry and query staleness are checked with an injected clock.",
        "use_when": [
          "You want to register microservice instances with an external registry backing service with a lease, extend them with HEARTBEAT, and cancel them when they are no longer needed.",
          "You want to query where a counterpart is and receive old instances too, without hiding them, marked with stale: true and effective_health: STALE.",
          "You want the configuration to decide which registries and services may be used, which endpoints may be registered and the ttl limit, and not to send anything outside that.",
          "You want lease expiry and query staleness to be checked with an injected clock rather than on the registry backing service's word."
        ],
        "do_not_use_when": [
          "You want purely local static name resolution. This Tsugite carries requests to an external registry backing service; local name resolution is within the scope of a separate Primitive.",
          "You want to use an endpoint obtained from a query directly as a genuine, healthy counterpart that may be called. This Tsugite does not check that (endpoint_identity is NOT_VERIFIED; correctness and authority are NOT_DERIVED).",
          "You want lease state to carry over after a restart. The state exists only inside this Tsugite, and after a restart it does not send HEARTBEAT or DEREGISTER for instances registered earlier.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels).",
          "You want a connection to a registry backing service that is ready to use. It has never connected to an actual registry backing service; the port implementation, credentials and configuration values will be settled at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks the configuration. If there is no configuration, or for a registry, service or endpoint not in the lists, a REGISTER without register_endpoints or max_ttl_seconds, or a QUERY without stale_after_seconds, it does not send.",
          "It checks the ttl. A ttl that is not an integer, a ttl above the configured limit, and a ttl or a configured limit above 86400 are not sent.",
          "It checks the clock. If the clock is missing or broken, it sends nothing and accepts no receipts.",
          "It checks lease handling. A REGISTER for an instance that still has a valid lease, a HEARTBEAT for a missing or expired lease, and a DEREGISTER for an instance this Tsugite did not register are not sent.",
          "It checks the form of references and of the request. References containing .., absolute paths, drives or control characters, a request_id that is not a Ref, a malformed service_name, an unknown op, and fields the op does not use are not sent.",
          "It matches the receipt. A spoofed request_digest, a mismatch in request_id, registry, service_name or op, a state outside the table, and an increased or unknown effect are not accepted, and it stops (HALTED).",
          "It matches the lease and query contents. A lease longer than the requested ttl, a lease not in timestamp form, a last_seen_at in the future, a query that lists the same instance twice, and a receipt whose observation time is before the request or later than now are not accepted.",
          "A receipt that derives correctness or permission (where correctness or authority is not NOT_DERIVED) is not accepted."
        ],
        "what_it_does_not_do": [
          "It does not check whether a listed endpoint is genuine (identity, certificate), or whether it is correct, healthy or may be called. observed_health only carries the registry backing service's observation as is. Checking identity is the role of, for example, the B-09 certificate and the caller's trust policy.",
          "Being able to register or query is not approval (approval: false). Whether a counterpart may be called is for an external decision and for people and organizations.",
          "It does not check whether the length of an instance's lease_expires_at is reasonable (the contract has no criterion for it). With a backing service that returns leases far in the future, an old instance may not show as old. It does not DEREGISTER other parties' instances.",
          "It does not check the correctness of the clock itself, or the actual drift from the registry backing service's clock.",
          "With a port that has no lookup, when a send whose result is unknown can no longer pass the checks, it stops with the send unresolved. It also does not accept a FAILED with an UNKNOWN effect and stops; whether the registration remains is checked by a person.",
          "It holds no credentials for authenticating with the registry backing service; the port holds them (HELD_BY_PORT). It does not change the contract either. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for using any specific registry backing service (such as a service mesh or a cloud's name resolution)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/service-registry/v1. Input: pass a RegistryRequest to submit(request) on createServiceRegistryClient({ cfg, port, clock }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries the facts for each op in its result (for QUERY, instances and endpoint_identity). Every result has approval: false. User provides: a port to the registry backing service (submit, and optionally lookup) and a clock. Side effect: NONE for QUERY, EXTERNAL_REVERSIBLE for the other three.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-02",
        "category": "N/連携基盤",
        "kind": "Component",
        "title": "API gateway routing — API gateway routing part (Component)",
        "summary": "The API gateway routing part (Component) is a part that bundles checking the gateway's route table locally (the route-config Primitive) and then applying it to the gateway (the gateway-config Tsugite). Changes are two-step: prepareApply and prepareRollback return the subject to be signed off by a person or organization (authority subject) and its digest, and apply and rollback are called with evidence that includes that digest. It does not send a change without evidence, a change different from the subject of the evidence, or a table the check refused (fail-closed). Being applied is not proof that the routing is correct and is not approval of the change.",
        "problem": "Behind the gateway, the aggregation point for authentication and traffic, it prevents applying a table that has not passed the check or a table different from the checked digest, applying a change without evidence from a person or organization or with sign-off given for a different change, and resending a change whose result is unknown as a different request.",
        "use_when": [
          "You want to check the route table locally (shape, references, conflicts, hidden routes) and APPLY to the gateway only tables that passed the check. Inject the check of route-config as checkRoutes and a client of the gateway-config Tsugite as gateway.",
          "You want to split a change into two steps. prepareApply and prepareRollback send nothing and return the subject to be signed off (gateway_ref, op, base_version_ref, and config_digest or rollback_to_version_ref) and its digest; apply and rollback take evidence from a person or organization that includes that digest (origin: HUMAN or ORGANIZATION, evidence_ref, subject_digest).",
          "You want the APPLY to carry the validation evidence (validation_evidence) that the Tsugite made from the output of the local check, and then to pass the evidence from a person or organization to the Tsugite before sending.",
          "When going back to a previous version (rollback), you also want the evidence from a person or organization passed to the Tsugite before sending.",
          "You want to observe the current version without effect (observe, STATUS) and, when rollout_of_request_id is passed, to receive whether that rollout has finished exactly as the Tsugite's result. observe can be sent even while a change is pending.",
          "You want to hold a change whose result is unknown as pending and resend it with resolvePending only as the same request. base_version_ref prevents a double change."
        ],
        "do_not_use_when": [
          "You want to use being applied (APPLIED) as proof that the routing is correct or as approval of the change. Every result carries approval: false, authority: NOT_DERIVED and traffic_correctness: NOT_DERIVED.",
          "You want to change routes without evidence from a person or organization. Route changes (APPLY, ROLLBACK) need evidence from a person or organization; this part does not create that evidence and receives it from the caller.",
          "You want this part to check the content of the evidence (who, and until when). This part only checks that the subject's digest matches and passes the evidence to the Tsugite.",
          "You need a part that does the request routing itself (directing traffic at the entrance). This part checks the route table and only has the bundled Tsugite send the change that alters the routing.",
          "You want to rely on the gateway reading the table by the same rules as the check, or computing the same digest. Neither is checked (the limit of route-config remains).",
          "You need a part checked against a real gateway. It has never connected to a real gateway or control plane.",
          "You want to continue a pending change across a restart. The state is only inside this process, and a restart loses the pending request.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed, a reference is invalid or a port function is missing, it stops with INVALID_OPTIONS, when the input is malformed, with INVALID_INPUT, and when a getter on the input or on a port's result throws an exception, with INSPECTION_FAILED.",
          "When the local check refuses the table, it stops with ROUTES_REFUSED (with route_errors attached), when the config_digest in the check's result is malformed (domain, algorithm, 64 hexadecimal digits, target), with CHECK_RESULT_INVALID, and when the check function throws, with PORT_FAILED, and it does not send the APPLY.",
          "When the evidence is missing, has missing or extra fields, has an origin other than HUMAN or ORGANIZATION, has an invalid reference, or has a subject_digest that is not 64 hexadecimal digits, it stops with NO_AUTHORITY_EVIDENCE, and when the evidence's subject_digest differs from the subject's digest recomputed on the spot (sign-off for a different change), with AUTHORITY_SUBJECT_MISMATCH, and it does not send the APPLY or ROLLBACK.",
          "When the Tsugite does not return the validation evidence, it stops with EVIDENCE_REFUSED, and when it does not return exactly true for recording the evidence, with AUTHORITY_REFUSED, and it does not send.",
          "When the Tsugite's result is unknown (UNKNOWN, an exception), it holds the change as pending with GATEWAY_UNKNOWN, and while it is pending, anything other than resolvePending and observe is stopped with CHANGE_PENDING. Calling resolvePending when nothing is pending returns NOTHING_PENDING.",
          "When the Tsugite did not send, it reports GATEWAY_REFUSED (with gateway_errors attached), and when the Tsugite stopped because the receipt could not be matched, GATEWAY_HALTED, without changing the state and without keeping a pending change.",
          "It sets applied to true only when the Tsugite's result has applied: true, and rollout_completed to true only when the Tsugite's result is exactly true. observe (STATUS) is sent without effect (intended_side_effect NONE).",
          "When timestamp does not return a date-time string or throws, it stops with CLOCK_FAILED, and when the previous operation has not finished, with BUSY (only one operation at a time)."
        ],
        "what_it_does_not_do": [
          "The API gateway routing part is not a Tsugite. It has no Request or Receipt of its own; the external effect of changing the routing is caused by the bundled gateway-config Tsugite under its own contract (gateway-config/v1). This part itself does not touch the outside.",
          "The meaning of the bundled parts (passing the check is not proof that the routing is correct; being applied is not approval) stays on each part's side.",
          "Being applied is not proof that the routing is correct and is not approval of the change (approval: false, authority: NOT_DERIVED, traffic_correctness: NOT_DERIVED, derives_authority: false).",
          "It does not create evidence from a person or organization. It receives the evidence for a route change from the caller and does not check the content of the evidence (who, and until when).",
          "Whether a rollout has finished after ACCEPTED, APPLYING or PARTIAL is only returned as the Tsugite's result when rollout_of_request_id is passed to observe; this part does not judge it.",
          "It has never connected to a real gateway or control plane. The unit tests and fuzzing use stand-ins, and the wiring with the code of the real two parts was checked against the Tsugite's test stand-in for the gateway.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular gateway product."
        ],
        "integration": "Create it with createGatewayRouting(options) in src/routing.js. Input: gateway_ref, request_prefix, ports and an optional timestamp. Returns: { ok, errors, instance }; each operation returns { ok, errors, approval: false, record_pending, last_change, … }. User provides (injected through ports): checkRoutes (the check of route-config) and gateway (the client of the gateway-config Tsugite: submit, and two functions that pass the validation evidence and the evidence from a person or organization). Side effect: caused by the bundled Tsugite (EXTERNAL_REVERSIBLE); credentials are held by the ports.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-03",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Webhook subscription registry — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks requests to register, update or revoke a Webhook subscription (WebhookSubscription) against the pinned list of Webhook destinations, sends them to the integration target, and matches the returned receipt against the contract. If the destination, a reference or the receipt is off, it does not send and does not accept (fail-closed).",
        "problem": "When notification destinations (Webhook destinations) are registered externally, it prevents an arbitrary callback URL from being injected, prevents the destination from being substituted through the receipt, and prevents mistaking \"registered\" for \"the notification was delivered\".",
        "use_when": [
          "You want to register and manage notification destinations (Webhook destinations) with an external integration target. Destinations are chosen only from a list pinned in advance.",
          "You want to match the result of subscribing (SUBSCRIBE), updating (UPDATE) or revoking (REVOKE) against the receipt's state (REGISTERED, UPDATED or REVOKED, or FAILED) before acting on it.",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup.",
          "You want further sending to stop once a receipt that cannot be matched arrives (HALTED after RECEIPT_REJECTED)."
        ],
        "do_not_use_when": [
          "You want to deliver Webhooks, verify their signatures or receive them. This Tsugite only handles registering subscriptions.",
          "You want to use a subscription receipt as evidence that a notification was delivered. A receipt can only speak to the registration stage (SUBSCRIPTION_REGISTERED).",
          "You want users to specify an arbitrary callback URL. It does not send to destinations that are not pinned.",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the destination. It does not send to a destination that is not in the pinned list of Webhook destinations (callback URL injection). An empty list allows nothing.",
          "It matches the destination in the receipt. If the destination has been substituted, it does not accept the receipt and stops (HALTED).",
          "It does not accept a subscription receipt that claims delivery or receipt by the other side. It also does not accept a receipt whose state does not match the operation (not SUBSCRIBE→REGISTERED, UPDATE→UPDATED or REVOKE→REVOKED, and not FAILED).",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept. The effect of a subscription is EXTERNAL_REVERSIBLE (NONE for FAILED).",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not deliver Webhooks, verify their signatures or receive them. That is outside the scope of this Tsugite (it only registers subscriptions).",
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (WebhookSubscription). Input: pass a WebhookSubscription request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries subscription_state, subscription_ref and lifecycle_stage. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_REVERSIBLE (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-04",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Data import (ETL) connector — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks data import requests (DataImport) from outside into your own system against the pinned sources, schemas and mappings, sends them to the integration target, and matches the returned receipt's counts and rejected-record references against the contract. A partial failure is returned as is, never collapsed into success or failure (fail-closed).",
        "problem": "When importing data from outside, it prevents importing from sources or mappings that are not pinned, and prevents fudging the counts or partial failures and treating the import as \"everything was imported\".",
        "use_when": [
          "You want to import data from outside into your own system (migration or sync), limited to the pinned sources, schemas and mappings.",
          "You want to match the import result against the counts (received = accepted + rejected) and the rejected-record references (rejected_record_refs) before acting on it.",
          "You want an import that partly failed to be received as a partial failure (PARTIAL).",
          "You want an irreversible import to be sent only when the settings explicitly allow it (allow_irreversible: true).",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to check whether the meaning of the imported data or the mapping is correct. This Tsugite looks only at counts and references (semantic_correctness: NOT_VERIFIED).",
          "You want to hand over the judgement of reconciling an import. Reconciliation is the role of the Kernel (reconcile-facts).",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks sources, schemas and mappings. Anything not in the pinned lists is not sent. An empty list allows nothing.",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "An irreversible import is sent only when the settings have allow_irreversible: true. Otherwise it is not sent.",
          "It matches the counts. It does not accept a receipt where received = accepted + rejected does not hold, where the number of rejections does not match the number of rejected-record references, or where the rejected-record references contain duplicates.",
          "It matches the result state. It does not accept a PARTIAL with only acceptances or only rejections, a receipt that says complete despite rejections, or a receipt that says FAILED despite acceptances.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept. The effect of an import is EXTERNAL_* (NONE for FAILED).",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not check the semantic correctness of the imported data or the correctness of the mapping (only counts and references).",
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (DataImport). Input: pass a DataImport request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries partial_state, the counts, rejected_record_refs and semantic_correctness: NOT_VERIFIED. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_* (NONE for FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-05",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Data export connector — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks data export requests (DataExport) from your own system to the outside against the pinned destinations, schemas and profiles, sends them, and matches the returned receipt against the contract. It returns \"generated\", \"delivered\" and \"accepted\" separately, and does not accept a receipt that does not match (fail-closed).",
        "problem": "When exporting your own data to the outside, it prevents sending to destinations that are not pinned, and prevents treating \"generated\" alone as \"delivered to the other side\" or \"accepted by the other side\".",
        "use_when": [
          "You want to export data from your own system to the outside, limited to the pinned destinations, schemas and profiles. The export selection can also be pinned, optionally (selection_refs).",
          "You want to handle the export result separately as generated (GENERATED), delivered (DELIVERED) and accepted (ACCEPTED). DONE comes with delivery_state, delivered and destination_accepted.",
          "You want an irreversible export to be sent only when the settings explicitly allow it (allow_irreversible: true).",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want to use \"generated\" (GENERATED) as evidence that the data reached the other side. This Tsugite returns the three stages separately.",
          "You want to check the semantic correctness of the exported data. Semantic correctness is the role of the Kernels and of people; this Tsugite only checks before sending and after receiving.",
          "You want to decide which integrations to allow. That decision is the role of the Kernel (policy-facts).",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks destinations, schemas and profiles. Anything not in the pinned lists is not sent. An empty list allows nothing.",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "An irreversible export is sent only when the settings have allow_irreversible: true. Otherwise it is not sent.",
          "It matches the stage and the effect. It does not accept a receipt that says generated (GENERATED) but claims an external effect, or one that says delivered (DELIVERED) but has no effect. GENERATED has effect NONE or LOCAL_REVERSIBLE; DELIVERED and ACCEPTED have EXTERNAL_*.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept.",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (DataExport). Input: pass a DataExport request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries delivery_state, delivered and destination_accepted. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: it differs by stage (NONE to EXTERNAL_*).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-06",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Schema registry — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks schema registration requests (SchemaRegistration) against the pinned list of schemas, sends them to the integration target, and matches the returned receipt against the contract. It does not mistake a successful registration for the Schema Authority or Canonical (fail-closed).",
        "problem": "When registering schemas to share contracts (types), it prevents registering schemas that are not pinned, and prevents treating \"registered\" as \"the schema has become the authority (Schema Authority) or Canonical\".",
        "use_when": [
          "You want to register only pinned schemas with the integration target, to share contracts (types).",
          "You want to match the registration result against the receipt's registration_state, schema_authority and canonical_status before acting on it.",
          "You want to match the registration effect as EXTERNAL_REVERSIBLE, and duplicates (DUPLICATE) and failures (FAILED) as no effect (NONE).",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want registration to make the schema the authority or Canonical. Registering a schema creates neither a Schema Authority nor Canonical.",
          "You want to decide which schema is the authority (Schema Authority). That is the role of the Kernels and of people, and the authority for this Tsugite's contract has not been set yet either (NOT_SPECIFIED).",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks schemas. Anything not in the pinned list of schemas is not sent. An empty list allows nothing.",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "It does not accept a registration receipt that claims to be the authority (Schema Authority) or claims to be Canonical.",
          "It matches digest targets. As read from the contract, the target of schema_digest is schema_identity and the target of content_digest is request_id.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept. The effect of a registration is EXTERNAL_REVERSIBLE (NONE for DUPLICATE and FAILED).",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "Registering a schema creates neither a Schema Authority nor Canonical.",
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (SchemaRegistration). Input: pass a SchemaRegistration request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries registration_state, schema_authority and canonical_status. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: EXTERNAL_REVERSIBLE (NONE for DUPLICATE and FAILED).",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-07",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Event schema evolution — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks schema compatibility check requests (SchemaCompatibility) against the pinned schemas, sends them to the integration target, and matches the returned receipt's compatibility claim and test result (test_result) against the contract. Compatibility UNKNOWN is not treated as compatible (fail-closed).",
        "problem": "When changing an event schema, it prevents treating a result whose compatibility has not been confirmed (UNKNOWN), or a result that failed its test, as \"compatible\" and so breaking backward compatibility.",
        "use_when": [
          "You want to match the result of a schema compatibility check before acting on it, to manage backward compatibility.",
          "You want something treated as compatible (compatible: true) only when a compatibility claim (BACKWARD, FORWARD or FULLY) and a test PASS are both present.",
          "You want to check compatibility only for pinned schemas (including event schemas).",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want compatibility UNKNOWN, or a claim whose test was not run (NOT_RUN), to be treated as compatible. This Tsugite does not treat them as compatible.",
          "You want to decide the semantic correctness of a schema or which schema is the authority (Schema Authority). That is the role of the Kernels and of people.",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks schemas. Anything not in the pinned list of schemas is not sent. An empty list allows nothing.",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "It does not accept a receipt with compatibility UNKNOWN as compatible, even if the test is PASS.",
          "It does not accept a receipt that says compatible (compatible: true) although the test failed (FAIL), or a contradictory receipt that pairs a compatibility claim (BACKWARD, FORWARD or FULLY) with FAIL.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept. The effect of a compatibility check is NONE.",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (SchemaCompatibility). Input: pass a SchemaCompatibility request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries compatibility, test_result and compatible. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-08",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "External OAuth connection management — Integration and ETL Tsugite",
        "summary": "The Integration and ETL Tsugite (継手, tsugite) checks external connection registration requests (ExternalConnectionRegister) against the pinned connections, credential references and scopes, sends them, and matches the returned receipt against the contract. It does not run the actual OAuth flow or issue or store tokens, and does not mistake being connected for business authorization (fail-closed).",
        "problem": "When registering a connection for an integration account, it prevents passing token or key values in place of references, prevents registering with scopes that are not allowed, and prevents treating \"connected\" as \"allowed for business\".",
        "use_when": [
          "You want to register an integration account connection, limited to the pinned connections (connection_refs), credential references (credential_refs) and allowed scopes (allowed_scopes).",
          "You want to pass credentials only as references, not as values (bytes).",
          "You want to match the registration result against the receipt's connection_state, credential_binding_state and business_authorization before acting on it.",
          "You do not want the same request to be sent twice. When a failure at the integration target leaves the result unknown, it is treated as UNKNOWN and resolved by a lookup."
        ],
        "do_not_use_when": [
          "You want it to run the OAuth flow, or to issue or store tokens. This Tsugite does not do that. Resolving what a credential reference points to is the role of the secret-ref / portable-credential-ref mechanism.",
          "You want to use a successful connection as business authorization. Deciding which integrations to allow is the role of the Kernel (policy-facts).",
          "You want to rely on it as a mechanism that recognizes and blocks token- or key-shaped values. This detection is a net that looks only at typical shapes and does not recognize every secret. The real guard is the pinned list of references.",
          "You want a connector dedicated to a specific SaaS. This Tsugite is not a per-SaaS connector; it applies the same checks to every integration target.",
          "You want an integration target you can use right away. No actual integration target is included, and where to connect has not been decided.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks connections, credential references and scopes. Connections or references not in the pinned lists, and scopes that are not allowed, are not sent. An empty list allows nothing.",
          "If a credential_ref is shaped like a token or key, it is not sent even if it is pinned.",
          "It does not accept a receipt that derives business authorization from a successful connection.",
          "It checks references. Empty references, references shaped like a path or containing \"..\", additional fields, and values that differ from the fixed values (const) are not sent.",
          "It does not accept a receipt that escalates the external effect, a receipt whose effect is UNKNOWN, or a receipt for a different request (spoofed request_digest). It stops (HALTED) on a receipt it cannot accept. The effect of registering a connection is NONE or LOCAL_REVERSIBLE.",
          "It never sends the same request_id twice. It does not send a request that reuses an idempotency key with different content (REFUSED). A failure at the integration target is treated as UNKNOWN and resolved by a lookup."
        ],
        "what_it_does_not_do": [
          "It does not run the actual OAuth flow, or issue and store tokens. Resolving what a credential reference points to is the role of the secret-ref / portable-credential-ref mechanism.",
          "The detection of token- or key-shaped values is a net that looks only at typical shapes; it does not recognize every secret.",
          "Deciding semantic correctness, the Schema Authority and business authorization is the role of the Kernels (reconcile-facts and policy-facts) and of people. This Tsugite only checks before sending and matches what it receives afterwards.",
          "Having carried, registered or connected something is not approval (every result carries approval: false).",
          "It does not include an actual integration target. Which SaaS or ETL platform to connect to, the port implementation and the lists to pin are undecided and will be settled at a separate gate when connecting.",
          "This Tsugite has no N-01 service discovery or N-02 API gateway function. N-01 is handled by the Tsugite service-registry-tsugite (contract service-registry/v1), and N-02 by the Component gateway-routing-component.",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/integration-etl/v1 (ExternalConnectionRegister). Input: pass a ExternalConnectionRegister request to submit(request) on createIntegrationClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; DONE carries connection_state, credential_binding_state and business_authorization. Every result has approval: false. User provides: a port to the integration target (submit, and optionally lookup) and the pinned lists in cfg. Side effect: NONE or LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-09",
        "category": "N/連携基盤",
        "kind": "Primitive",
        "title": "Fetch record — Fetch record (fetch.json) part (Primitive)",
        "summary": "The Fetch record (fetch.json) part (Primitive) is a standalone part that creates and checks fetch.json (schema erabazu.raw.fetch/1), a record that holds only the facts of when, from where and what was fetched. The fetch_record inside it has the same keys and the same patterns as RawFetchReceipt.fetch_record in the contract raw-fetch/v1. It canonicalizes the record with RFC 8785 and returns its sha256. It fetches nothing.",
        "problem": "It is a foundation for the provenance of ingestion. A record it cannot accept is not silently skipped; it is stopped with a fixed code and location, and traces of presigned URLs (://, X-Amz-Signature, X-Amz-Credential, Signature=, the form of a URL with a query) are not let into any part of the record. The record's hash is the sha256 of the canonical bytes and does not depend on key order or white space.",
        "use_when": [
          "You want to create a fetch.json record from the facts of a fetch (source, platform_profile, retention_class, kind_declared, fetch_state, fetch_record) (buildFetchRecord). The part adds schema and folder_stamp.",
          "You want to copy the fetch_record of a receipt from the contract raw-fetch/v1 (RawFetchReceipt) into the record unchanged.",
          "You want to read a stored record back, check it, and get the same canonical and record_sha256 (checkFetchRecord; it rebuilds folder_stamp from fetched_at and compares).",
          "You want a fetch that stopped while has_more was still true (PARTIAL) to be kept as a record of what was obtained.",
          "You want to call it locally as deterministic processing that has no clock, no random numbers and no outside connection. fetched_at is passed by the caller."
        ],
        "do_not_use_when": [
          "You want to confirm that a file exists, or that byte_size and sha256 match the content. That is the role of I-10; this part does not check it.",
          "You want to confirm that the record contains no secrets. The net for URL and signature traces is coarse; it cannot find secrets that do not contain :// (a token or the value of a key itself) or URLs whose traces have been broken up.",
          "You want to confirm that the fetch really was FETCHED or PARTIAL, or that fetched_at is correct as the fetch time or is not in the future. It accepts them as copies and checks only form and calendar.",
          "You want to decide the retention policy (the meaning and period of retention_class). It is not decided here.",
          "You want to find overlapping names on a file system that does not distinguish uppercase and lowercase. It accepts a and A as different names.",
          "You want to confirm that the declared media type matches the content or that charset is correct. It records the declaration as given (reading is done by I-09 and I-10).",
          "You use it after the contract raw-fetch/v1 has been revised without copying the patterns again. The pattern strings are copies from the contract, and after a revision they need to be copied again and re-checked. Versions other than Node.js v24.21.0 have not been checked either (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with NOT_AN_OBJECT when the record (or the build input) is not an object, and with NOT_CANONICALIZABLE (with an I-08 code in detail) when it contains values that cannot be canonicalized with RFC 8785 (fractions, undefined, getters, Date and so on). It does not call getters, and it checks on the canonicalized copy.",
          "It stops with UNKNOWN_FIELD when there is a key that is not defined (including schema or folder_stamp passed to build), and with MISSING_FIELD when a required key is missing. fetch_params_digest and files[].file_ref are also required.",
          "It stops with INVALID_SCHEMA, INVALID_SOURCE, INVALID_PLATFORM_PROFILE, INVALID_RETENTION_CLASS, INVALID_KIND_DECLARED or INVALID_FETCH_STATE when schema, source, platform_profile, retention_class, kind_declared or fetch_state is not a defined value or form. It does not accept states that have no fetch_record (NOT_READY, FAILED and so on).",
          "It stops with INVALID_FETCHED_AT when fetched_at does not match the contract's pattern (1970 or later, UTC Z, no leap second) or is a day that does not exist in that month (February 30, February 29 in a common year and so on), and with FOLDER_STAMP_MISMATCH when folder_stamp differs from the value rebuilt from fetched_at.",
          "It stops with INVALID_DIGEST when the form of a Digest (keys, algorithm, value, domain) is wrong or files[].sha256.target differs from name; with INVALID_FILES when files is empty, has the wrong keys, or byte_size is not an integer of 0 or more; with INVALID_FILE_NAME when name does not match the contract's pattern (including names containing ..); with DUPLICATE_FILE_NAME when the same name appears two or more times; and with INVALID_MEDIA_TYPE when declared_media_type is not of the contract's form or is longer than 255 characters.",
          "It stops with INVALID_PAGES when pages is empty, seq does not go up by 1 from 0, cursor does not chain, or has_more does not fit the state (for FETCHED only the last is false; for PARTIAL all are true).",
          "It stops with LINEAGE_REQUIRED when platform_profile is amazon-sp-api-reports or amazon-pay-reports but lineage is missing, and with API_VERSION_REQUIRED when it is stripe but api_version is null (for both FETCHED and PARTIAL). When lineage has the wrong form, it is INVALID_LINEAGE.",
          "It stops with URL_WITH_QUERY_REFUSED when any string in the record contains the form of a URL with a query, and with URL_FORM_REFUSED when it contains ://, X-Amz-Signature, X-Amz-Credential or Signature= (case-insensitive; the search runs in linear time). When it stops, it returns the first problem, in a fixed order, as code and path (a JSON Pointer)."
        ],
        "what_it_does_not_do": [
          "Stripe (Amazon) is named only as a connection target; this is not a partnership or an endorsement.",
          "It fetches nothing. It does not read or write files either.",
          "That a record was accepted does not mean that the fetch was correct, that the files exist, that the digests match the content, that it contains no secrets, or that it may be accepted.",
          "Among the contract's runtime rules, it does not check those that cannot be confirmed without the request (whether the target and value of fetch_params_digest match the request), or matters outside the record.",
          "The readability check (I-09), the seal (H-11) and the retention policy remain outside this part.",
          "Creating a record is not an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for the record format of a particular fetching tool."
        ],
        "integration": "Call buildFetchRecord({ source, platform_profile, retention_class, kind_declared, fetch_state, fetch_record }) and checkFetchRecord(record) in src/fetch-record.js (they do not throw). check takes a record of 8 keys including schema and folder_stamp. It returns { ok: true, errors: [], record, canonical, record_sha256 } or { ok: false, errors: [{ code, path }] }. There are no side effects, no state, no outside connection and no credentials, and there is no port for the caller to provide. Its only dependency is the Node.js built-in crypto.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-10",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Raw data fetch (Stripe) — Raw Data Fetch (Stripe) Tsugite",
        "summary": "The Raw Data Fetch (Stripe) Tsugite (継手, tsugite) fetches the response body of each page from Stripe's list APIs exactly as received and returns it together with a fetch record (fetch_record). It checks the request (RawFetchRequest) before carrying it to the port, and matches the receipt (RawFetchReceipt) against the received bytes. It does not read the content, and neither normalizes nor judges it (fail-closed).",
        "problem": "Behind an integration import, it prevents losing sight of whether the data is exactly as received. It recomputes the length and sha256 of the received bytes and compares them with the record, checks the cursor chain, the API version and the declarations, does not keep presigned URLs, and does not mistake a fetch for a judgement or an acceptance.",
        "use_when": [
          "You want to keep the responses of Stripe's list APIs (charges, payment_intents, refunds, balance_transactions, payouts, disputes, invoices and events) exactly as received.",
          "You want to accept the received bytes only after confirming that their byte_size and sha256 are the same as in the fetch record (fetch_record).",
          "You want to be able to continue after stopping partway through the pages. The result.resume_cursor of a PARTIAL result becomes the starting_after of the next request.",
          "You want to copy the fetch record as it is into the N-09 fetch record (fetch.json). result.fetch_record is exactly as in the receipt."
        ],
        "do_not_use_when": [
          "You want to read, type or normalize the content of the responses. Mapping to a common form, judging duplicates and reconciliation are the role of other layers and of people and rules.",
          "You want to have a report created (CREATE_REPORT). This Tsugite sends only op FETCH, and sends no other profile.",
          "You want a template for a Stripe API client. This Tsugite is not such a template.",
          "You want retention periods to be enforced. Enforcing retention is the role of storage (C-01) and its partitions.",
          "You want a fetch mechanism you can use right away. No actual fetch from Stripe has been made (0 times); the port implementation, the credentials and the setting values will be decided at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the request. It sends only a request with profile stripe, op FETCH, limit 1 to 100, and only one of starting_after and ending_before, and only when the account (account_refs) and the credentials (credentials) are pinned in the settings. Otherwise it does not send (REFUSED).",
          "Credentials are only references. It uses only references declared with purpose FETCH and scope READ_ONLY, and does not accept a configuration in which credentials contains the same reference as a signing key (signing_key_refs).",
          "It copies the received bytes before matching them. It accepts them only when they have the same order, names and count as files[], byte_size is the actual number of bytes, and sha256 is the sha256 of the actual bytes.",
          "It matches pages. It accepts only an unbroken cursor chain in which seq starts at 0 and increases by 1, where FETCHED ends with has_more false and PARTIAL stops holding the cursor for the continuation. FETCHED and PARTIAL require an api_version string.",
          "It does not record presigned URLs. presigned_url_recorded is false, and no value in the request or the receipt may contain ://, X-Amz-Signature, X-Amz-Credential or Signature= (case-insensitive).",
          "It accepts only a receipt whose declarations (kind_declared, retention_class and identity_basis) have the same values as the request.",
          "It does not accept a receipt with an inconsistent state, a receipt that escalates the effect (FETCH is NONE in every state), a receipt whose effect is unknown, or a receipt for a different request, and it stops (HALTED after RECEIPT_REJECTED).",
          "It does not send different content under the same request_id. A failure at Stripe (the port) is treated as UNKNOWN; because the request only reads, the same request may be sent again."
        ],
        "what_it_does_not_do": [
          "Stripe is named only as a connection target; this is not a partnership or an endorsement.",
          "It does not check the content of the received bytes (form, number of records, correctness, or whether they contain PII). It also does not check whether the declarations match the content, or whether declared_media_type matches the actual type of the content.",
          "The actual behavior of Stripe's list APIs (sort order, the direction of ending_before, has_more) was only imitated with a stand-in and has not been compared with the actual Stripe. It also does not check the form of the API version value.",
          "For times (observed_timestamp and fetched_at), it checks only the form and that the date and time exist; it does not check whether they are correct or stale. How many pages to fetch at once and the time budget are the role of the port.",
          "Records of what has been matched exist only inside this Tsugite and are lost on restart. Writing out fetch.json and the payloads is the role of N-09 and of storage.",
          "A successful fetch is not approval, and does not mean the content is correct, accepted or reconciled (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/raw-fetch/v1 (RawFetchRequest / RawFetchReceipt, profile stripe, op FETCH). Input: pass a RawFetchRequest to submit(request) on createRawFetchClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: a Stripe port (submit returns { receipt, files }) and cfg (account_refs, credentials and signing_key_refs). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-11",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Raw data fetch (Amazon SP-API reports) — Raw Data Fetch (Amazon SP-API Reports) Tsugite",
        "summary": "The Raw Data Fetch (Amazon SP-API Reports) Tsugite (継手, tsugite) fetches report documents (such as gzipped TSV) from the Amazon SP-API Reports API exactly as received and returns them together with a fetch record (fetch_record and lineage). It checks the request (RawFetchRequest) before carrying it to the port, and matches the receipt (RawFetchReceipt) against the received bytes. It does not read the content, and neither decompresses, normalizes nor judges it (fail-closed).",
        "problem": "Behind a report import, it prevents losing sight of whether the data is exactly as received. It carries documents still gzipped, compares length and sha256, lineage and declarations, does not keep presigned URLs, and does not have a report it requested created twice.",
        "use_when": [
          "You want to fetch an automatically generated report (a settlement report) by report_id after the REPORT_PROCESSING_FINISHED notification (received through a separate port) (op FETCH).",
          "You want to have an on-demand report created and receive its report_id (op CREATE_REPORT). Only the report types listed in the setting allowed_report_types may be created.",
          "You want to accept received documents only after confirming that their byte_size and sha256 are the same as in the fetch record and that a GZIP document has not been decompressed.",
          "You want to copy the fetch record as it is into the N-09 fetch record (fetch.json). result.fetch_record is exactly as in the receipt and includes the lineage."
        ],
        "do_not_use_when": [
          "You want to decompress documents, read the TSV, or map it to a common form. That is the role of other layers, and judgement is the role of people and rules.",
          "You want to have an automatically generated settlement report (GET_V2_SETTLEMENT_REPORT_…) created. Those types cannot be put in the settings.",
          "You want to receive the REPORT_PROCESSING_FINISHED notification. The receiver for notifications is a separate port.",
          "You want a template for an SP-API Reports client. This Tsugite is not such a template.",
          "You want retention periods (such as the period for customer PII) to be enforced. Enforcing retention is the role of storage (C-01) and its partitions.",
          "You want a fetch mechanism you can use right away. No actual fetch from SP-API has been made (0 times); the port implementation, the credentials and the setting values will be decided at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks the request. It sends only requests with profile amazon-sp-api-reports, and only when the account (account_refs) and the credentials (credentials) are pinned in the settings. It does not send a report type that is not allowed, or different content under the same request_id (REFUSED).",
          "Credentials are only references. It uses only references declared with purpose FETCH and scope REPORTS_ONLY, and does not accept a configuration in which credentials contains the same reference as a signing key (signing_key_refs).",
          "It copies the received bytes before matching them. It accepts them only when they have the same order, names and count as files[], byte_size is the actual number of bytes, and sha256 is the sha256 of the actual bytes. A GZIP document is accepted only when it is declared as application/gzip and starts with the gzip header (1f 8b).",
          "It matches the lineage. FETCHED and PARTIAL require a lineage, and for FETCH it does not accept a lineage.report_id that differs from the request's report_id. The lineage may not contain a document URL.",
          "It does not record presigned URLs. presigned_url_recorded is false, and no value in the request or the receipt may contain ://, X-Amz-Signature, X-Amz-Credential or Signature= (case-insensitive).",
          "It matches the effect. It accepts REPORT_REQUESTED only when it claims exactly EXTERNAL_REVERSIBLE; every other state must be NONE. It does not accept a receipt that leaves unclear whether a report was created, and it stops.",
          "A CREATE_REPORT whose result is unknown is remembered as unresolved and resolved by sending the same request again (searching first through port.lookup, if present). It does not have a report created twice, and it sends no other request until it is resolved.",
          "It accepts only a receipt whose declarations (kind_declared, retention_class and identity_basis) have the same values as the request. It stops after a receipt it cannot match (HALTED after RECEIPT_REJECTED)."
        ],
        "what_it_does_not_do": [
          "Amazon is named only as a connection target; this is not a partnership or an endorsement.",
          "It does not check the content of the received bytes (form, number of records, correctness, or whether they contain PII). It also does not check whether the declarations match the content, or whether declared_media_type matches the actual type of the content (for GZIP, it looks only at the first 2 bytes, the gzip header).",
          "The actual behavior of SP-API (report processing time, handling of CANCELLED and FATAL, the lifetime of document URLs, whether GZIP is used) was only imitated with a stand-in and has not been compared with the actual SP-API.",
          "An unresolved CREATE_REPORT is remembered only inside this Tsugite and is lost on restart. Sending again through a port without lookup may cause one more report to be created.",
          "Which types can be created on demand is left to the setting allowed_report_types. It does not know of any automatically generated type other than the settlement report.",
          "A successful fetch is not approval, and does not mean the content is correct, accepted or reconciled (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/raw-fetch/v1 (RawFetchRequest / RawFetchReceipt, profile amazon-sp-api-reports). Input: pass a RawFetchRequest to submit(request) on createRawFetchClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: an SP-API port (submit, which returns { receipt, files }, and optionally lookup) and the lists in cfg. Side effect: EXTERNAL_REVERSIBLE for CREATE_REPORT only.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "N-12",
        "category": "N/連携基盤",
        "kind": "Tsugite",
        "title": "Raw data fetch (Amazon Pay reports) — Raw Data Fetch (Amazon Pay Reports) Tsugite",
        "summary": "The Raw Data Fetch (Amazon Pay Reports) Tsugite (継手, tsugite) fetches the settlement reports that the Amazon Pay Reports API generates automatically for settlements, by report_id after the notification, exactly as received, and returns them together with a fetch record (fetch_record and lineage). It checks the request (RawFetchRequest) before carrying it to the port, and matches the receipt (RawFetchReceipt) against the received bytes. It does not have reports created (fail-closed).",
        "problem": "Behind a settlement report import, it prevents losing sight of whether the data is exactly as received. It carries settlement reports exactly as received, compares length and sha256, lineage and declarations, does not keep short-lived download URLs, and does not mistake a fetch for a judgement or an acceptance.",
        "use_when": [
          "You want to fetch a settlement report by its report_id after receiving the settlement report notification (through a separate port) (op FETCH). While the report is being processed, NOT_READY is returned; for a report that does not exist, REJECTED is returned.",
          "You want to accept a received settlement report only after confirming that its byte_size and sha256 are the same as in the fetch record.",
          "You want to fetch without keeping the download URL (which expires in 30 seconds) in the record. The port uses the URL immediately and discards it; it is not passed to the Tsugite.",
          "You want to copy the fetch record as it is into the N-09 fetch record (fetch.json). result.fetch_record is exactly as in the receipt and includes the lineage."
        ],
        "do_not_use_when": [
          "You want to have a report created or scheduled. Settlement reports are generated automatically, and this Tsugite does not send CREATE_REPORT (it is invalid under the contract).",
          "You want to read the CSV or map it to a common form. That is the role of other layers, and judgement is the role of people and rules.",
          "You want to receive the settlement report notification. The receiver for notifications is a separate port.",
          "You want a template for an Amazon Pay Reports client. This Tsugite is not such a template.",
          "You want retention periods to be enforced. Enforcing retention is the role of storage (C-01) and its partitions.",
          "You want a fetch mechanism you can use right away. No actual fetch from Amazon Pay has been made (0 times); the port implementation, the credentials and the setting values will be decided at a separate gate when connecting."
        ],
        "what_it_checks": [
          "It checks the request. It sends only a request with profile amazon-pay-reports, op FETCH and a report_id only, and only when the account (account_refs) and the credentials (credentials) are pinned in the settings. It does not send CREATE_REPORT, any other profile, or different content under the same request_id (REFUSED).",
          "Credentials are only references. It uses only references declared with purpose FETCH and scope READ_ONLY or NO_READ_ONLY_SCOPE_OFFERED (meaning the other side has stated that it offers no read-only scope). It does not accept a configuration in which credentials contains the same reference as a record signing key (signing_key_refs).",
          "It copies the received bytes before matching them. It accepts them only when they have the same order, names and count as files[], byte_size is the actual number of bytes, and sha256 is the sha256 of the actual bytes. When GZIP is stated, it accepts them only when they are declared as application/gzip and start with the gzip header (1f 8b).",
          "It matches the lineage. FETCHED and PARTIAL require a lineage, and for FETCH it does not accept a lineage.report_id that differs from the request's report_id.",
          "It does not record presigned URLs. presigned_url_recorded is false, and no value in the request or the receipt may contain ://, X-Amz-Signature, X-Amz-Credential or Signature= (case-insensitive).",
          "It accepts only a receipt whose declarations (kind_declared, retention_class and identity_basis) have the same values as the request.",
          "It does not accept a receipt with an inconsistent state, a receipt that escalates the effect (FETCH is NONE in every state), a receipt whose effect is unknown, or a receipt for a different request, and it stops (HALTED after RECEIPT_REJECTED).",
          "A failure at Amazon Pay (the port) is treated as UNKNOWN. Because the request only reads, the same request may be sent again."
        ],
        "what_it_does_not_do": [
          "Amazon is named only as a connection target; this is not a partnership or an endorsement.",
          "It does not check the content of the received bytes (form, number of records, correctness, or whether they contain PII). It also does not check whether the declarations match the content, or whether declared_media_type matches the actual type of the content (when GZIP is stated, it looks only at the first 2 bytes, the gzip header).",
          "The actual behavior of the Reports API (report processing, the lifetime of download URLs, the values of compressionAlgorithm) was only imitated with a stand-in and has not been compared with the actual Amazon Pay.",
          "It has not been checked whether Amazon Pay credentials can have a read-only scope. The actual purpose of the keys is checked by the port.",
          "Records of what has been matched exist only inside this Tsugite and are lost on restart. Writing out fetch.json and the payloads is the role of N-09 and of storage. For times, it checks only the form and that the date and time exist; it does not check whether they are correct or stale.",
          "A successful fetch is not approval, and does not mean the content is correct, accepted or reconciled (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/raw-fetch/v1 (RawFetchRequest / RawFetchReceipt, profile amazon-pay-reports, op FETCH). Input: pass a RawFetchRequest to submit(request) on createRawFetchClient({ cfg, port }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: an Amazon Pay port (submit returns { receipt, files }) and cfg (account_refs, credentials and signing_key_refs). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "O-01",
        "category": "O/検索・索引・取り出し",
        "kind": "Tsugite",
        "title": "Indexing, search and retrieval — Index, Search and Retrieval Tsugite",
        "summary": "The Index, Search and Retrieval Tsugite (継手, tsugite) checks requests for index writes (UPSERT, tombstone UPSERT and DELETE), searches (KEYWORD, VECTOR, HYBRID and STRUCTURED) and retrievals (the source's digest and provenance) against the contract and the settings, carries them to the port of a replaceable index backend, and matches the returned receipt. It does not treat what is found as fact (fail-closed).",
        "problem": "Behind \"it came up in the search\", it prevents losing sight of the index revision and freshness. It carries requests narrowed by the settings, compares the digest of the query document and the determinism of cursors, and does not mistake retrieved results for verified facts or for an authority.",
        "use_when": [
          "You want to carry index writes, searches and retrievals to an index backend (by default the user's SQLite FTS5 part; it is replaceable) with the same checks applied.",
          "You want to search with exact-match filtering (STRUCTURED). The sha256 of the query document canonical({filters:[{field, op, value}]}) is compared with query_digest before the document is passed on.",
          "When continuing through pages, you want to use only a cursor received in an ANSWERED answer for the same index, revision and query.",
          "When erasing personal information, you want to overwrite the index entry with a tombstone (an UPSERT with tombstone true and no content_digest)."
        ],
        "do_not_use_when": [
          "You want to use retrieved results as verified facts or as an authority. Retrieved is not true, similar is not a verified fact, and rank is not authority. Verifying and judging results is the role of external decision-makers and of people and organizations.",
          "You want to receive the bytes of the retrieved content. Only the digest and the provenance pass through this Tsugite; whether the content matches the digest is checked by the receiving side.",
          "You want a template for using a specific search engine, vector database or SQLite extension. This Tsugite is not such a template.",
          "You want to use it on top of a category and contract that are fixed as Canonical. Category O and the contract search-retrieval/v1 are DESIGN_CANDIDATE, and this Tsugite is also a candidate built on them.",
          "You want an index backend you can use right away. No call to an actual backend has been made (0 times); the port implementation and the setting values will be decided at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "Settings only narrow and never widen. It does not send requests outside index_profiles, index_ops, retrieval_modes, max_page_size, structured_fields and source_prefixes (if DELETE is removed, DELETE is not sent). If the settings contain an unknown key, it sends nothing.",
          "It checks the STRUCTURED document. It does not send when the form (one or more filters; op EQ, GTE or LT; value a string; field in the setting structured_fields) or the sha256 does not match query_digest. For results, it accepts only score 1.",
          "It checks cursors. It does not send a cursor for a different index_revision or a different query, or a cursor it has not received. It does not accept a non-deterministic cursor, the same cursor on the first page and a following page, more results than page_size, or duplicates of the same source.",
          "It matches search answers. An answer that is not ANSWERED must carry neither results nor a cursor, and is accepted only when its freshness is UNKNOWN. Result items are only {source_ref, score, provenance_ref}; fields such as verified are not accepted.",
          "It matches the effect. INDEXED and DELETED are EXTERNAL_REVERSIBLE, search and retrieval are NONE, and UNKNOWN is allowed only for FAILED on index writes. It does not accept a receipt that escalates the effect, a receipt for a different revision or op, or a receipt for a different request, and it stops (HALTED).",
          "An index write whose result is not visible (a port failure, or a FAILED receipt with effect UNKNOWN) is kept as unresolved. Before sending again, it reads the index entry with EntryReadRequest / EntryReadReceipt (port.readEntry), and does not send again if the write has taken effect. When it cannot read, it does not send again, and it sends nothing else until the write is resolved."
        ],
        "what_it_does_not_do": [
          "It does not check whether the ACL was applied before results were returned, whether the freshness (freshness_state) is real, or whether results really match or are relevant (these are what the backend reports; results are carried as NOT_VERIFIED).",
          "It does not check whether the backend compared STRUCTURED values by value, or whether filter values fit the declared types (the backend answers with FILTER_INVALID).",
          "The meaning of VECTOR and HYBRID (which embedding, how results are combined) is not in the contract; the score is carried only as a likelihood. Vector search is not made a requirement of the backend.",
          "Cursor matching and records exist only inside this client and are lost on restart. A cursor from before a restart cannot continue the pages; the search starts again from the first page.",
          "It does not check whether a readEntry answer really is the current entry in the index. With a port without readEntry, it does not send again after FAILED or UNKNOWN, and a person checks what remains in the index.",
          "It does not detect personal information written into the doc_ref, acl_ref or provenance_ref strings. Keeping personal information itself out of the index is an operational rule.",
          "Having carried a request or added an entry to the index is not approval, and does not mean the content was verified (every result carries approval: false). It does not change the contract, and the schema authority for the contract is NOT_SPECIFIED.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/search-retrieval/v1 (Request and Receipt for Index, Query, Retrieve and EntryRead). Input: pass a request, and for STRUCTURED the query document, to submit(request, query) on createSearchRetrievalClient({ cfg, port, clock? }). Returns: one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED; every result has approval: false. User provides: an index backend port (submit, and optionally lookup and readEntry) and the lists in cfg. Side effect: EXTERNAL_REVERSIBLE for index writes; NONE for search and retrieval.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "O-02",
        "category": "O/検索・索引・取り出し",
        "kind": "Component",
        "title": "Fetch and transaction index — Fetch and transaction index (fetch.db, txn.db) part (Component)",
        "summary": "The Fetch and transaction index part (Component) is a part that bundles the indexing and search Tsugite (O-01), the record check (the N-09 part (Primitive)) and the transaction event ID (the G-12 part (Kernel)) and builds two indexes. Into the fetch index it puts every raw fetch, and into the txn index the transaction date, transaction amount, counterparty and raw references, with UPSERT through O-01. fetch.json is checked with N-09 and txn_id with G-12, and nothing is put in when they disagree. Queries are STRUCTURED, and the results are txn_id candidates. It does not send DELETE. The index is an aid to finding things, and its results are not facts.",
        "problem": "It is a part for the index behind searching the books. It prevents putting unchecked records or transactions whose txn_id does not match into the index, putting a pii counterparty as is into an append-only index, erasing index entries with DELETE, and reading a query the index did not answer as \"a success with no results\".",
        "use_when": [
          "You want to check the fetch.json record with N-09 and put one fetch (source, folder_stamp, fetched_at, retention_class, the probe state and version, the number of files) into the fetch index with UPSERT (indexFetch). doc_ref is raw/<source>/<folder_stamp>.",
          "You want to recompute the txn_id of a transaction in the common form (erabazu.common.txn/1) with G-12 and put only the transactions that match into the txn index (indexTxn). The document is occurred_on, amount_key, currency, counterparty_key and raw_refs, and doc_ref is txn_id.",
          "You want to search transactions by transaction date (EQ, GTE, LT), amount (EQ, GTE, LT on amount_key, combined with EQ on the same currency), and currency and counterparty (EQ only), and receive txn_id candidates (queryTxn).",
          "You want to build query values by turning an amount into a 58-character fixed-width amount_key whose string order is the same as numeric order, and a counterparty into a counterparty_key (a value starting with \"id:\" or \"name:\"; sha256 for pii) (amountKey, counterpartyKey).",
          "You want to remove a pii transaction from an UPSERT-only index. It overwrites the txn index entry with a tombstone (an UPSERT with tombstone true and no content_digest) (tombstoneTxn). A tombstoned transaction is not put back in by this instance.",
          "You want to resend an index write whose result is unknown (including a tombstone) as the same request (resolvePending)."
        ],
        "do_not_use_when": [
          "You want to use the query results as facts about the transactions that meet the conditions, or as a complete list. The results are candidates (NOT_VERIFIED); whether a matched transaction really meets the conditions and whether the index has no gaps are as stated by the backend.",
          "You want to treat the sha256 of a pii counterparty as anonymization. The sha256 has no salt (secret key), so it can be found by trying candidate names or ids one by one (it is pseudonymization, not anonymization). Dates, amounts, currency and raw references go into the index as they are, even for pii transactions.",
          "You want to compare amounts across currencies. Filtering on amount_key is meaningful only within the same currency, and it does not convert currencies.",
          "You want full-text search. Full-text search (FTS5) is on the backend side; this part only sends structured filters. There is no operation in v1 for querying the fetch index.",
          "You want a tombstone to also delete the raw data in storage. A tombstone only overwrites the index entry. It does not check whether storage really deleted the raw data. Entries in the fetch index have no tombstone operation.",
          "You want to prevent a tombstoned transaction from being put back in after a restart or from another process. The record of tombstoned txn_id values is only inside this instance. Where to keep that record is up to the caller's operations.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When options are missing or malformed or the two profiles are the same, it stops with INVALID_OPTIONS; when the input is malformed, with INVALID_INPUT; when a transaction record is not in a shape usable for the index, with INVALID_TXN; and when a query is malformed (for example, amount_key without an EQ on currency), with INVALID_QUERY (with the position in where).",
          "When N-09 does not accept fetch.json, it reports FETCH_RECORD_REFUSED, and when N-09 throws or its answer is inconsistent, CHECK_PORT_FAILED; and it sends nothing. When a different fetch.json comes for the same folder_ref, or a different document for the same probe_version, it does not send (FETCH_INDEX_CONFLICT) (a correction uses a new probe_version).",
          "When G-12 does not accept the input, it reports TXN_ID_REFUSED; when G-12 throws or its answer is inconsistent, TXN_ID_PORT_FAILED; and when the recomputed txn_id differs from the record, TXN_ID_MISMATCH (with recomputed_txn_id attached); and it does not send. G-12 receives only copies of the 5 facts.",
          "When a txn_id sent as pii is sent or tombstoned in another partition, it reports TXN_CLASS_CONFLICT, and when there is an attempt to put a tombstoned txn_id back in, TXN_TOMBSTONED; and it does not send.",
          "When the result of an index write is unknown, it holds it as pending with INDEX_UNKNOWN and accepts no operations while pending (INDEX_PENDING). When the Tsugite did not send, it reports INDEX_REFUSED, and when the entry was not indexed, the answer cannot be matched, or it has a different doc_ref, a different op, a different index_revision or DELETED, INDEX_FAILED.",
          "When the result of a query is unknown, it reports QUERY_UNKNOWN; when the Tsugite did not send, QUERY_REFUSED; and when the results fail the result check (score 1, NOT_VERIFIED, the version sent, the shape and duplicates of txn_id) or a tombstoned txn_id is in the answer, QUERY_FAILED. When the index did not answer (query_state is not ANSWERED), it returns QUERY_NOT_ANSWERED with query_state.",
          "In amountKey, when the amount is outside the shape or range (38 digits, scale 0 to 18), it reports INVALID_AMOUNT; in counterpartyKey, when the counterparty is malformed, INVALID_COUNTERPARTY; and when the partition is none of the 3, INVALID_RETENTION_CLASS. When timestamp does not return an existing date-time, it sends nothing (CLOCK_FAILED), and when the previous operation has not finished, it reports BUSY."
        ],
        "what_it_does_not_do": [
          "The Fetch and transaction index part is not a Tsugite. It has no Request or Receipt of its own; index writes and queries are made by the bundled O-01 Tsugite under its own contract (search-retrieval/v1) (queries are NONE). This part itself does not touch the outside.",
          "The meaning of the bundled parts (indexed ≠ the content was checked, similar ≠ fact, the record was accepted ≠ the fetch is correct, the ID matches ≠ the transaction exists) stays on each part's side. The index backend (SQLite, FTS5) and full-text search are on the backend side.",
          "The results are neither facts nor approval (approval: false, authority: NOT_DERIVED, result_verification: NOT_VERIFIED, derives_authority: false).",
          "It does not send DELETE. It sends only IndexRequest with UPSERT and QueryRequest with STRUCTURED.",
          "For transaction records, it checks only the fields used by the index and the set of keys. Checking the content, such as line items, is the job of G-13. retention_class is used as passed by the caller, and whether it matches the raw partition is not checked. Passing the document content to the backend is the job of the wiring between the caller and the backend.",
          "In this part's tests it is not connected to the real O-01, N-09 or G-12. The unit tests and fuzzing use stand-ins. The wiring check passed, but the wiring for tombstones, query_state and some other paths is not in that check. It is not connected to a real index backend. The state is only inside the process and is lost on restart.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for a particular search engine or database."
        ],
        "integration": "Create it with createLedgerIndex(options) in src/ledger-index.js; it returns { ok, errors, instance }. options are the profiles and revisions of the two indexes, request_prefix, ports and an optional timestamp. The operations are indexFetch, indexTxn, tombstoneTxn, queryTxn, resolvePending, status, amountKey and counterpartyKey, and the results carry approval: false. Ports: index.submit (the client of the O-01 Tsugite), checkFetchRecord (N-09) and computeTxnId (G-12). The field types of the txn index are declared with TXN_FIELD_TYPES.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-01",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Contract and input check — Contract and input check part (Kernel)",
        "summary": "The Contract and input check part (Kernel) is a standalone part that, from a schema's id and digest, the schema, a value (a JSON string or an already-parsed value) and a declaration of capabilities, outputs as a fact whether the value conforms to the schema (conforms). It handles only a fixed subset of schema keywords; a schema containing other keywords, or a schema whose digest does not match, is not trusted and is not treated as conforming. Issues are listed with their locations (JSON Pointer). The fields inference and advice are always null.",
        "problem": "Checking the form of input is always needed behind the screen. It prevents treating a value as conforming without knowing which schema it was checked against, overlooking duplicate keys in JSON, and processing that never ends because of catastrophic backtracking in a pattern's regular expression (ReDoS).",
        "use_when": [
          "You want to output as a fact whether a value conforms to a schema the caller passes (type, properties, required, additionalProperties: false, enum, const, items, minItems/maxItems, minLength/maxLength, minimum/maximum, pattern) (checkContract).",
          "You want to confirm the identity of the schema before checking. If schema_ref.digest does not equal \"sha256:\" followed by the hex SHA-256 of the RFC 8785 canonical form, the value is not checked, the result is SCHEMA_UNKNOWN, and conforms is false. The digest alone can be produced with schemaDigest({ schema }).",
          "You want a JSON string (value_text, up to 1 MiB in UTF-8) read by the part's own strict reader, and duplicate keys returned as DUPLICATE_KEY with their locations. For duplicates, the later value is checked, as with JSON.parse.",
          "In the same call, you also want to receive the missing fields (missing) and the required capabilities that are not declared (capabilities_missing: the set capabilities_required minus capabilities_declared).",
          "You want missing input made need_input rather than treated as empty. When there is no digest, or when capabilities_required is present and capabilities_declared is not, state is need_input.",
          "You want to receive issues in a fixed order, up to 1000, and to know from issues_total and issues_truncated that the list was cut.",
          "You need byte-for-byte identical output from the same input without using the clock, random numbers or the environment's locale. The same schema gives the same digest regardless of key order."
        ],
        "do_not_use_when": [
          "You need full JSON Schema validation. It does not handle the reference keyword ref, allOf/anyOf/oneOf, format, if/then, the null type, arrays of types, tuple items, fractional ranges for numbers and so on; these give UNSUPPORTED_KEYWORD or INVALID_SCHEMA.",
          "You want it to confirm that a declaration of capabilities is genuine (the authenticity of authority). This part only outputs the difference between the declared sets; the Host confirms authenticity.",
          "You want to use back-references, lookbehind, lookahead, nested quantifiers, \\p, case-insensitive matching, the m flag or the s flag in a pattern. Forms it does not handle give SCHEMA_PATTERN_UNSAFE and do not conform.",
          "You want it to judge whether a value is correct for the business (the meaning of the schema).",
          "You want duplicate keys checked on an already-parsed value (value). Duplicate keys are not visible in value, and duplicate_keys_checked is false. Non-integer numbers are accepted but match no type and no enum.",
          "You expect the same results as the I-01 input schema validation part. Both the range of keywords and the form of the results differ, and they are not compatible.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is malformed (an unknown key, a getter, no schema_ref or schema, both or neither of value_text and value, a malformed schema_ref), it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED.",
          "When a known keyword is malformed (type is not one of the 6 names, duplicates in required, an empty enum, items that is not a schema, a negative count, a pattern that cannot be read as a regular expression with the u flag, and so on), it stops with INVALID_SCHEMA. Unknown keywords and values of additionalProperties other than false become UNSUPPORTED_KEYWORD issues, and the schema is not trusted.",
          "A pattern is limited to 128 characters, 4,096 instructions and {n,m} up to 1000, and back-references, lookbehind and nested quantifiers become SCHEMA_PATTERN_UNSAFE. Matching uses the part's own linear-time matcher (a Pike VM); the total work is counted in advance, and when it exceeds 50,000,000 it stops with RESOURCE_LIMIT before matching begins.",
          "When there is no digest the state is need_input, and when the digest differs the state is done; in both cases there is a SCHEMA_UNKNOWN issue and the value does not conform. The only accepted notation is \"sha256:\" followed by lower-case hex; upper case or a missing prefix does not match. When capabilities_declared is absent, the state is also need_input.",
          "When value_text cannot be read as JSON (RFC 8259) (including a BOM, a leading zero, control characters or invalid escapes), it stops with INVALID_JSON, and a lone surrogate stops with LONE_SURROGATE, both with the character position offset. Exceeding 64 levels of nesting, 1 MiB or the limit on the number of values gives RESOURCE_LIMIT.",
          "When value is not plain JSON (undefined, a function, BigInt, NaN, Infinity and so on), it stops with INVALID_VALUE, and when a list of capabilities is not an array of unique strings of 1 to 128 characters (up to 256 items), with INVALID_CAPABILITIES. \"__proto__\" is treated as an ordinary key, and the prototype is not rewritten.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0. Pattern matching was compared against the Node.js RegExp (u flag) on the oracle side."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null; the result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The results are facts about whether a value conforms to a fixed subset of a schema. They do not show that the meaning of the value is correct, that its use is permitted, or that authority is genuine, and they are neither a data validation service nor a decision on authority.",
          "The meaning of pattern matching was compared with the Node.js v24.21.0 RegExp (u flag) using generated patterns and strings. It is not a proof covering all of ECMAScript regular expressions.",
          "The limit on matching work (pattern_steps) and the limit on the number of values in value (value_nodes) were confirmed by unit tests only (the fuzzing does not generate them).",
          "It holds no business-specific rules. The caller passes the schema.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call checkContract({ schema_ref, schema, value_text | value, capabilities_declared?, capabilities_required? }) in src/contract-check.js (it does not throw exceptions). Returns: { ok: true, errors: [], facts } (state, conforms, schema_trusted, issues, missing and so on, with inference: null and advice: null) or { ok: false, errors: [{ code, path, ... }] }. schemaDigest({ schema }) outputs only the digest. There is no side effect, no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-02",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Mapping, transformation and reconciliation — Mapping, transformation and reconciliation part (Kernel)",
        "summary": "The Mapping, transformation and reconciliation part (Kernel) is a standalone part that copies records of a source schema into the target form using a mapping table (source location to target location, with a transform) and reconciles the counts, keys and sums. It applies only mappings that are confirmed (confirmed) and not AI-derived (ai_candidate), and lists candidates as candidates. Conflicting mappings are not applied, and missing, unknown, zero and empty are counted separately. The result of the transformation is a candidate, not a final result. The fields inference and advice are always null.",
        "problem": "When copying data across for a migration or an integration, it prevents applying unconfirmed mappings or AI proposals as facts, filling values that are not in the table with guesses, merging two values with different meanings (such as paid and slot reserved) into one, and sums going wrong through floating point or rounding.",
        "use_when": [
          "You want to copy records into the target form using only confirmed mappings (reconcileMap). Mappings whose status is candidate and mappings whose origin is ai_candidate are not applied; they are listed in candidates_not_applied with a reason (STATUS_CANDIDATE, ORIGIN_AI_CANDIDATE).",
          "You want to limit transforms to four (copy for copying, enum_map for replacement by a table, unit for exact decimal multiplication, decimal_scale for aligning decimal places). There is no rounding; rounding is \"none\" only.",
          "You want to reconcile counts, keys and sums. Sums add the decimal strings and integers at the sum_paths locations exactly with BigInt, and the results of comparing source, target and expected are output together with the number of values that could not be added.",
          "You want missing (missing_source_values), unknown (unknown_values; copied as { \"unknown\": true }), zero and empty counted separately. Zero, empty strings and empty arrays are copied as values.",
          "You want to receive, in lists and without anything hidden, unmapped fields, values not in the table, values that could not be transformed, and duplicate and missing keys (unmapped_fields, unmapped_values, transform_failures, duplicate_keys, missing_keys).",
          "You need byte-for-byte identical output from the same input without using the clock, random numbers or the environment's locale. There is no external boundary and no state."
        ],
        "do_not_use_when": [
          "You want it to judge whether the meaning of a mapping is correct (whether same-name-different-meaning cases were really told apart). SAME_NAME_DIFFERENT_TARGET only lists mappings from the same source location to different target locations, and it does not find the same meaning under different names.",
          "You want it to confirm that a confirmation (status: confirmed, origin) is genuine. That depends on the caller's records. When a person adopts an AI proposal, the caller sets origin to human or registered when passing it.",
          "You need locations that go into arrays by index, or a transform per array element. In v1, an array is copied whole or counted as an unmapped leaf.",
          "You want to check a system of units (dimensions, correctness of the conversion). It only compares the from and to name strings, and the correctness of factor is the caller's responsibility.",
          "You need rounding. If decimal_scale would need to discard non-zero digits, the value is counted in transform_failures as INEXACT_SCALE.",
          "You want to apply or store the results of the transformation. This part only outputs the copied candidates; atomic application is a role outside this part.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is malformed (an unknown key, a required key absent, an id or version not 1 to 256 characters, duplicates in sum_paths), it stops with INVALID_ARGUMENT, when inspecting the values itself throws an exception, with INSPECTION_FAILED, and when a value is not in JSON Pointer form (more than 16 levels, more than 1024 characters, a lone surrogate), with INVALID_PATH.",
          "When the mapping table is malformed, it stops with INVALID_MAP, and when the kind of a transform is not one of the four or rounding is not \"none\", with UNSUPPORTED_TRANSFORM (rounding is never implicit).",
          "When a record is not a plain JSON object (null, a non-integer number, a lone surrogate, a form resembling { \"unknown\": true }), it stops with INVALID_RECORDS, when expected is malformed, with INVALID_EXPECTED, and when a limit is exceeded, with RESOURCE_LIMIT (with limit). The prototype is not rewritten through a \"__proto__\" key.",
          "Among the candidate mappings to apply, it lists as conflicts two for the same target (TWO_SOURCES_ONE_TARGET), nested target locations (TARGET_PATH_OVERLAP), the same source to different targets (SAME_NAME_DIFFERENT_TARGET) and different from values for unit on the same source (UNIT_MISMATCH), and does not apply them.",
          "When, in one enum_map table, two or more source values map to the same target value, it lists them as MERGED_ENUM_VALUES and does not apply the mapping unless merge_confirmed: true is present.",
          "Values not in the enum table are not filled by guesses but counted in unmapped_values; non-decimal values (NOT_A_DECIMAL) and values that would lose digits (INEXACT_SCALE) are counted in transform_failures; in both cases that field of converted is not created. If no mapping that reads key_path is applied, state is need_input (KEY_NOT_MAPPED).",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null; the result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The results are candidates copied mechanically from the mapping table and the records, and facts of reconciliation. They are not the correctness of a mapping's meaning, the adoption of a data migration, or permission to apply it. Adoption and application are done by the Host, the Runtime and a person.",
          "It does not make AI-derived mappings (ai_candidate) into facts. converted is a candidate, not finalized or applied.",
          "It holds no business-specific mapping tables of meaning. The caller passes the mapping table.",
          "The limit on the number of values in records (record_nodes) was confirmed only by unit tests and rare fuzz generation, and the limit on expected.keys (100,000) has not been tested.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call reconcileMap({ source_schema_id, target_schema_id, source_version, map, records, key_path, sum_paths?, expected? }) in src/map-reconcile.js (it does not throw exceptions and does not modify the input). Returns: { ok: true, errors: [], facts } (converted, candidates_not_applied, conflicts, reconciliation, need_input and so on, with inference: null and advice: null) or { ok: false, errors: [{ code, path[, limit] }] }. There is no side effect, no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-03",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Diff, inverse operation and conflict check — Diff, inverse operation and conflict check part (Kernel)",
        "summary": "The Diff, inverse operation and conflict check part (Kernel) is a standalone part that, for JSON documents, outputs the diff between two versions (a patch with old values), the inverse of a patch, a candidate with a patch applied, and an undo plan per purpose (intent). Before applying, it checks that the base version matches, protected locations and the expected prior values; for undo, it lists, without hiding them, overlaps with later separate changes and changes that cannot be inverted. It does not finalize or store anything. The fields inference and advice are always null.",
        "problem": "In the diffs behind \"undo\", it prevents applying a patch to an old version, rewriting protected locations, breaking later separate changes when undoing, and getting by with inverting only part of a change that cannot be inverted.",
        "use_when": [
          "You want the diff between two versions of JSON output as a patch with old values (add, remove, replace) (diff). Object keys are walked in UTF-16 code unit order.",
          "You want the inverse of a patch (invert). Operations that cannot be inverted (remove or replace without old) are all listed as NOT_INVERTIBLE, and in that case no inverse patch is output.",
          "You want a candidate with a patch applied, without finalizing it (apply). If base_revision differs from document_revision it gives STALE_BASE, and if a protected location is touched it gives PROTECTED_PATH; all of these are listed before applying. committed is always false.",
          "You want an undo plan per purpose (intent) (plan_undo). It inverts the changes with the same intent, newest first, and chains them; locations that overlap later changes that are not undone are listed as LATER_CHANGE_OVERLAPS.",
          "You want intent assignments made by AI (intent_origin: ai_candidate) kept out of the facts and separated into candidate_intent_changes. Those changes are not included in the undo, and they are treated as kept changes that overlaps are checked against.",
          "You need byte-for-byte identical output from the same input without using the clock, random numbers or the environment's locale. There is no external boundary and no state."
        ],
        "do_not_use_when": [
          "You want to finalize or store changes, or to assign version numbers. Atomic application, storage and version numbering are roles outside this part, and apply only outputs a candidate.",
          "You need character-level diffs inside strings. If strings differ, the result is a single replace (editing character fragments of a document is within the scope of P-09).",
          "You want to detect insertion or movement of array elements. It does no alignment (LCS and so on), so an insertion at the front becomes a series of replaces of the following elements.",
          "You want to use it as RFC 6902 JSON Patch. It does not handle move, copy, test or the \"-\" for the end of an array, and it also differs in carrying old values and in add not placing a value at a key that already exists.",
          "You want it to map natural-language purposes to changes. The caller decides the assignments, and assignments made by AI are passed as ai_candidate.",
          "You want to use it to undo sending something outside or a payment. These are out of scope.",
          "You want to see semantic dependencies (one change relying on another). It looks only at overlapping locations (semantic dependencies are within the scope of P-04).",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the fixed keys (an unknown key, a missing key, a getter, a non-enumerable property, a symbol key), it stops with INVALID_ARGUMENT, when inspecting the values itself throws an exception, with INSPECTION_FAILED, and when operation is none of the four, with UNSUPPORTED_OPERATION.",
          "When a document, value or old is not a JSON value (undefined, NaN, a lone surrogate, a key named __proto__, a sparse array, an array subclass, a class instance and so on), it stops with INVALID_VALUE.",
          "When a patch op is move, copy or test, it stops with UNSUPPORTED_OP, when a patch is malformed (the path of add or remove is the root), with INVALID_PATCH, and when a JSON Pointer is malformed (including a token named __proto__), with INVALID_POINTER.",
          "When a version is not a well-formed string of 1 to 128 characters, it stops with INVALID_REVISION, when origin or intent_origin is outside the vocabulary, with INVALID_ORIGIN, when a change is malformed, with INVALID_CHANGE, when two changes have the same change_id, with DUPLICATE_CHANGE_ID, and when a limit (10,000 operations, depth 64, 200,000 values and so on) is exceeded, with RESOURCE_LIMIT (with limit).",
          "In apply, STALE_BASE and PROTECTED_PATH (equal, below, above, or a location moved by re-indexing an array) are all listed before applying, and if either is present nothing is applied. Otherwise the operations are applied in order on a copy, stopping at the first PRECONDITION_FAILED (old does not match, or a value already exists at the add location) or PATH_NOT_FOUND (including \"-\" and leading zeros).",
          "In plan_undo, breaks in the history (HISTORY_GAP), differences from the current version (STALE_BASE), changes that cannot be inverted (NOT_INVERTIBLE) and overlaps with later changes (LATER_CHANGE_OVERLAPS) are listed in conflicts, and if there is even one, no undo_patch is output. If there is no change to undo, the result is no_matching_change.",
          "Unit tests and fuzzing confirmed that the candidate of apply(base, diff(base, target)) equals target and that the candidate of apply(target, invert(diff(base, target))) equals base.",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null; the result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The results are a presentation of facts (diffs, inverses, candidates, plans), not the finalizing or storing of changes or the execution of an undo. Which change belongs to which purpose, and whether to adopt a candidate, are decided outside this part (by the user or the Runtime).",
          "Array re-indexing is determined from the form of the token only. Objects with numeric-looking keys are also treated as re-indexing, so it stops more often than strictly needed.",
          "It does not use need_input. facts.state is always done, and the reasons for stopping are listed in refusals, conflicts and not_invertible (in that case ok is true).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call patchConflict({ operation, ... }) in src/patch-conflict.js. diff, invert, apply and planUndo can also be called directly (they do not throw exceptions and do not modify the input). Returns: { ok: true, errors: [], facts } (state: \"done\", operation and the facts of each operation, with inference: null and advice: null) or { ok: false, errors: [{ code, path }] } (RESOURCE_LIMIT also carries limit). The caller holds the history, the versions and the storage. There is no side effect, no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-04",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Provenance and dependency graph — Provenance and dependency graph part (Kernel)",
        "summary": "The Provenance and dependency graph part (Kernel) is a standalone part that, from versioned nodes, edges (registered ones and AI candidates, ai_candidate) and a set of changes, outputs separately the nodes reached downstream through registered edges only (impacted, with the shortest path) and the nodes that cannot be reached without candidate edges (candidate_impacted). It also outputs as facts cycles, edges to absent nodes or with mismatched versions, nodes outside the caller's permission, and nodes that were not reached. The fields inference and advice are always null.",
        "problem": "The scope of a change's impact cannot be seen from a table. It prevents mixing AI-candidate dependencies with registered dependencies as facts, silently following missing edges or version mismatches, and treating nodes that were not reached as \"unrelated\".",
        "use_when": [
          "From the changes (changed), you want the nodes the impact reaches through registered edges only, with depth and the shortest path (path, edges) (analyzeProvenance). Among paths of the same length, the one whose sequence of node ids is smallest is chosen.",
          "You want the nodes that cannot be reached without AI-candidate edges (ai_candidate) separated into candidate_impacted, and to also receive the indexes of the candidate edges in the path. impacted and candidate_impacted do not overlap.",
          "You want cycles received separately as strongly connected components of registered edges only (cycles) and as components that first appear or grow with candidate edges (candidate_cycles).",
          "You want edges to absent nodes (NODE_ABSENT) and edges with mismatched versions (VERSION_MISMATCH) received as missing, without following them, and state set to need_input.",
          "For nodes outside the caller's permission (allowed: false), you want only the id output in not_allowed even when they are reached, without kind or version. Reachability continues through such a node.",
          "You want to set the search depth (max_depth, 1 to 256, 256 if absent) and to know from depth_limit_hit (separately for the registered search and the search including candidates) that the search was cut by depth.",
          "You need byte-for-byte identical output from the same input, regardless of the order of nodes or keys, without using the clock, random numbers or the environment's locale."
        ],
        "do_not_use_when": [
          "You want it to judge whether there is an impact or whether a fix is needed. It only outputs the facts of reachability and gaps.",
          "You want to show that nodes that were not reached are unrelated. unreached is the fact that they were not reached in the search, not proof that they are unrelated (unreached_meaning: not_reached).",
          "You want the way edges are followed to vary by edge type (kind). It does not read kind and follows every kind the same way.",
          "You want to see the order of versions. from_version and to_version are only compared as strings for equality.",
          "You want it to confirm that the allowed marks are correct (the authenticity of authority), or to block the contents of nodes outside the permission. Both are the Host's role, and the Host does not pass the contents of nodes outside the permission.",
          "You want the part to decide the direction of edges. An edge { from, to } is passed in the direction \"a change to from affects to\" (for provenance, origin to derivative).",
          "You use it on a version other than Node.js v24.21.0 (NOT_VERIFIED), or you want to rely on the time and memory for inputs at the full limits. Inputs at the full limits were checked only once each in unit tests, and time and memory were not measured."
        ],
        "what_it_checks": [
          "When the input is not plain data (getters, class instances, sparse arrays, extra properties on arrays, symbol keys, non-enumerable properties), or when there is an unknown key, a missing key, null, a value outside the vocabulary (an origin other than registered or ai_candidate and so on), an id that is not 1 to 128 characters, or a lone surrogate, it stops with INVALID_INPUT.",
          "When max_depth is not an integer from 1 to 256, it stops with INVALID_INPUT, when inspecting the values itself throws an exception (a Proxy trap, a revoked Proxy), with INSPECTION_FAILED, and when ids overlap, with DUPLICATE_ID.",
          "When a limit (100,000 nodes, 400,000 edges, 100,000 changed, a total of 4,000,000 node ids in the output paths) is exceeded, it stops with RESOURCE_LIMIT, putting which limit in limit. It does not turn a failure into an empty success.",
          "When from_version or to_version is written on an edge and differs from that node's version, it outputs VERSION_MISMATCH in missing, and when an end node is absent, NODE_ABSENT. Such an edge is not followed and is not included in the cycle calculation.",
          "A changed node with a mismatched version is used as a starting point, because the change is a fact stated by the Host, and VERSION_MISMATCH is also output. The changed nodes themselves are not put into impacted, candidate_impacted or unreached.",
          "When it stops, it returns only the first problem found, as { code, path, limit? }. path is a JSON Pointer (the empty string for the whole input).",
          "Fuzzing that compares the results against an independent oracle rewritten from the rules without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null; the result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false). It does not turn AI-derived items (ai_candidate) into facts.",
          "The results are a presentation of facts about reachability and gaps, not a judgement of whether there is an impact or whether a fix is needed. Nor do they judge impact or plan fixes on anyone's behalf. Final authority, blocking and application are the roles of the Host and the Runtime.",
          "When cut by max_depth, a node that lies deep along registered edges can end up in candidate_impacted through a short path over candidate edges (in that case depth_limit_hit.registered is true).",
          "The input and output forms are those set in v1, and acceptance by the side that uses them has not yet been done.",
          "It uses no files or network communication and holds no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call analyzeProvenance({ nodes, edges, changed, max_depth? }) in src/provenance-graph.js (it does not throw exceptions and does not modify the input). Returns: { ok: true, errors: [], facts } (state, impacted, candidate_impacted, cycles, candidate_cycles, missing, not_allowed, unreached, depth_limit_hit and need_input, with inference: null and advice: null) or { ok: false, errors: [{ code, path, limit? }] }. There is no side effect, no state, no external connection, no credentials and no dependency package, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-05",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Constraints, assignment and time slots — Constraints, assignment and time slots part (Kernel)",
        "summary": "The Constraints, assignment and time slots part (Kernel) is a standalone part that, from tasks written in whole minutes (duration, whether indivisible, the minimum when split, prerequisites, resource, time window) and slots (start, end, resource), runs a bounded search in a fixed order and returns as facts an assignment plan, the reasons it cannot be done, and whether the search limit was reached. infeasible is returned only when it is proven; stopping at the limit is kept apart as search_limit. What is unknown is not guessed but made need_input, and malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "When a schedule is built, it prevents shrinking an indivisible task to fit into a short slot, calling something \"impossible\" when the search was only cut off partway, presenting a plan as optimal when its optimality has not been proven, and filling unknown durations or time windows with guesses.",
        "use_when": [
          "You want an assignment plan from tasks (0 to 200) and slots (0 to 200) for the objective set in objective (solveSchedule). There are three objectives: feasible (a plan that places every task), min_unassigned (the fewest tasks left out) and earliest_finish (the earliest latest end when every task is placed).",
          "You want an indivisible task (indivisible: true) placed whole in one slot, at its full duration.min. If the overlap of the slot and the time window is shorter than min, the task is not put in that slot (a 20-minute indivisible task is not put in a 10-minute slot).",
          "You want a splittable task split and placed so that every part is at least splittable_min and the parts add up to exactly duration.min. Parts of the same task do not overlap in time, and part is numbered from 1 in time order.",
          "You need an assignment that keeps the prerequisites (requires), resources (resource) and time windows (window). A prerequisite task ends before every part of this task, and if a prerequisite task is not placed, this task is not placed either. A resource match is required only when both the task and the slot have a resource.",
          "You want to receive, with reason codes, that a plan cannot be made. The static reasons (NO_SLOTS, NO_SLOT_FOR_RESOURCE, WINDOW_EMPTY, INDIVISIBLE_TOO_LONG, CAPACITY_TOO_SMALL, PRECEDENCE_CYCLE) and the marker SEARCH_EXHAUSTED, which shows the search was carried through to the end, are put in reasons.",
          "You want to bound the search steps with max_nodes (1 to 1,000,000) and receive, kept apart, whether the limit was reached, through search_limit_reached and status (search_limit), and the optimality, through optimality (proven or not_proven).",
          "You want to receive, kept apart, whether AI-derived tasks (origin: ai_candidate) are included, through origin on each assignment row, ai_candidate_tasks and plan_includes_ai_candidate.",
          "You need byte-for-byte identical output from the same input, regardless of the order of arrays or keys, without using the clock, random numbers or the environment's locale."
        ],
        "do_not_use_when": [
          "You want it to decide whether to adopt a plan, re-split tasks, or whether durations are correct. The result is the facts of an assignment plan, the reasons it cannot be done and the search limit; it is not a decision to adopt or carry out anything.",
          "You want the part to decide how tasks are split or how long they take. The caller decides both and passes them. This part does not create tasks and does not guess durations.",
          "You want it to stop different tasks from being placed at the same time in two overlapping slots of the same person. Slots are treated as independent containers. For one person's schedule, the caller passes slots that do not overlap.",
          "You want to handle travel or set-up time between tasks, calendars or time zones, or times finer than a minute. Times and lengths are only whole minutes from 0 to 100,000,000.",
          "You need objectives such as weights, priorities, soft constraints or the least lateness against deadlines. There are only three objectives; anything else stops with UNSUPPORTED_OBJECTIVE. min_unassigned counts the number of tasks, not their length.",
          "You want to use the slack of a task that can take longer than min. duration.max is only checked for its form; the length placed is always duration.min.",
          "You want to solve a large problem within a fixed time. The search is exponential, and a large problem can end in search_limit. Speed is not promised. It always stops when it reaches max_nodes.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding exactly the four keys { tasks, slots, objective, search }, it stops with INVALID_ARGUMENT; when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED; and when it is not plain data (getters, class instances, sparse arrays, extra properties on an array, symbol keys, a __proto__ key, non-enumerable keys), with NOT_PLAIN_DATA. It checks the values after copying them into plain data, and the values it checks are the same values the search uses.",
          "When a task is malformed (an unknown key, a missing key, a malformed id, origin or indivisible, a duration.min below 1, a max below min or a non-integer, a malformed unknown marker, splittable_min on an indivisible task, a splittable_min above min, duplicated or non-array requires, a time window whose end is not after its start, or a negative value), it stops with INVALID_TASK. It does not accept null.",
          "When a slot is malformed (an end not after its start, a non-integer, a negative value and so on), it stops with INVALID_SLOT; when search is not { max_nodes }, with INVALID_SEARCH; when objective is none of the three objectives, with UNSUPPORTED_OBJECTIVE; when two tasks or two slots have the same id, with DUPLICATE_ID; and when requires points to the id of a task that does not exist, with UNKNOWN_TASK_REF.",
          "When a limit is exceeded (200 tasks, 200 slots, 200 prerequisites per task, max_nodes 1,000,000, times and lengths of 100,000,000 minutes, 4096 array elements, 4 levels of nesting), it stops with RESOURCE_LIMIT, putting which limit in limit. When it stops, it returns { code, path, limit? }, where path is a JSON Pointer.",
          "When duration or window is the unknown marker { unknown: true }, it does not guess: it sets state to need_input and lists what is missing in need_input. It does not search; status, assignment and objective_value are null and reasons is empty (no partial result is returned).",
          "For feasible and earliest_finish, if there is even one static reason, it does not search and returns infeasible (nodes_explored is 0). If the limit is reached and there is no plan, it does not say infeasible but returns search_limit, and a plan left after the limit was reached is marked not_proven (when search_limit_reached is true, nodes_explored = max_nodes).",
          "Fuzzing that compares the results against an independent oracle written from the rules without using the helper functions in src (including brute force over small problems and a recheck of the assignment) confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only the facts of the assignment. The fields inference and advice are always null, and no string in the output contains words of correction or priority. The result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false). It does not turn AI-derived tasks (ai_candidate) into facts and does not judge whether they are correct.",
          "The result presents the facts of an assignment plan, the reasons it cannot be done and the search limit; it is not a scheduling decision and does not carry anything out. Whether to adopt a plan, re-split tasks, or whether durations are correct is decided by the user outside this part.",
          "That the search is complete (that a plan placing every task is found if one exists) was checked by a left-packing argument and by 1,000,000 comparisons against brute force over small problems. It has not been formally proven.",
          "The brute-force comparison covers only small problems, up to 4 tasks, 3 slots and times around 20. infeasible and proven for larger problems have not been checked by brute force.",
          "nodes_explored is this part's own fixed way of counting and cannot be compared with the step counts of other solvers. Which of several plans with the same value is returned is decided by the search order.",
          "The input and output forms and the way steps are counted were defined in v1; there is no existing implementation (new).",
          "It has no external boundary, no state, no clock and no random numbers. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call solveSchedule({ tasks, slots, objective, search }) in src/constraint-schedule.js (it does not throw exceptions and does not modify the input). Returns: { ok: true, errors: [], facts } (state, status, optimality, assignment, unassigned, reasons, need_input, search_limit_reached and so on, with inference: null and advice: null) or { ok: false, errors: [{ code, path, limit? }] }. Side effect: NONE. There is no state, no external connection, no credentials and no dependency package, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-06",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Model and bounded rule evaluation — Model and bounded rule evaluation part (Kernel)",
        "summary": "The Model and bounded rule evaluation part (Kernel) is a standalone part that evaluates, within fixed limits, a model (an expression written as a JSON tree) and bounded rules (a small DSL of conditions and effects only) passed by the caller. evaluateModel produces a value and a unit for each output by decimal BigInt arithmetic (34-digit precision, half_even), and evaluateRules and runRules produce the legal moves for each role, the next state (a candidate), and the end and its result. It does not accept loops, recursion or code execution. UNKNOWN is not guessed but made need_input, and malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "When expressions for estimates or determinations are evaluated, it prevents floating-point errors entering the values, division by zero or out-of-range results slipping in as 0 or NaN, missing input being filled with guesses and a partial calculation being output, and rule evaluation turning into unending loops or code execution. Random numbers are also made only from an explicit seed, so the same input and seed produce the same result.",
        "use_when": [
          "You want to produce, as decimal strings, the value and the canonical unit of each output from a model (params declaring the parameters and their units, and outputs holding an expression for each output) and inputs (value, unit and origin). The expression operations are + - * / min max neg, round (scale 0 to 34; half_even, half_up, floor or ceil) and rand.",
          "You want to vary one parameter over 1 to 1000 points with sweep and list the values at each point (the number of points × the number of nodes is at most 1,000,000).",
          "You want expressions containing random numbers evaluated reproducibly from an explicit seed (an integer from 0 to 2^53−1). The generator is xorshift128, whose 4 words are made from the seed by SplitMix64, and it restarts for each output and for each sweep point.",
          "You want to calculate with consistent unit dimensions. Units are compared as sets of exponents per symbol; * and / add and subtract exponents, and + - min max require the dimensions to match.",
          "From bounded rules written as roles, moves (when and effects of each move) and end, and a state, you want evaluateRules to produce the legal moves for each role; when a move is given, the next state (a candidate), or ILLEGAL_MOVE with the first condition that did not hold; and the end and its result.",
          "You want runRules to apply a sequence of moves in order up to max_steps (1 to 1000), stop at the first move that could not be applied, and receive the state before that move as final_state.",
          "You want to receive, separately, whether an AI-derived model, input, sweep or rules (origin ai_candidate) were used, through basis (registered_only or includes_ai_candidate) and the locations in candidate_sources."
        ],
        "do_not_use_when": [
          "You want to use the results as a forecast of actual demand. The results are the calculation of the expressions passed. It does not check whether the model's expressions, the rules or the input values are correct.",
          "You want the part to convert units (for example, m and km). Different units of the same dimension are treated as different symbols. To multiply by a factor, write it in the expression.",
          "You need cryptographic random numbers or random numbers that have passed statistical tests. xorshift128 is not for cryptographic use, and no statistical tests have been run.",
          "You handle rules with moves that take arguments, simultaneous turns, probabilities over hidden information, or time-outs. Bounded rules do not have these.",
          "You want to write loops, recursion, expression evaluation or code execution into the rules. Anything outside the vocabulary is stopped with UNSUPPORTED_CONSTRUCT.",
          "You want to put booleans, null or fractional numbers in the state. State values are only integers, strings, arrays, objects and the UNKNOWN marker { unknown: true }.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the fixed keys, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays, null or a __proto__ key, and checks the values after copying them into plain data.",
          "When the input is malformed (an unknown key, a missing key, an origin outside the vocabulary, the form of a name, a duplicate, the form of a JSON Pointer and so on), it stops with INVALID_INPUT; when a decimal string is not in the fixed form, has more than 34 significant digits, is outside the range (0 or 10^-100 ≤ |value| < 10^100) or is \"-0\", with INVALID_DECIMAL; and when a unit string is malformed or the combined exponents exceed 64, with INVALID_UNIT.",
          "When a name refers to an undeclared parameter or role, it stops with UNKNOWN_NAME; when the unit dimensions of an addition, subtraction, min or max differ, or the unit of an input or sweep has a different dimension from the declaration, with UNIT_MISMATCH; when an expression contains rand but there is no seed, with SEED_REQUIRED; and when an expression operator, a rounding mode, or the op of a condition or effect is outside the vocabulary, with UNSUPPORTED_CONSTRUCT.",
          "When a limit is exceeded, it stops with RESOURCE_LIMIT and puts the name of the limit in limit. The limits include expression depth 64, 10,000 nodes, 1000 sweep points, a state of 256 KiB and depth 64, strings of 4096 characters, effect values of 4 KiB, and max_steps 1000. The size of the state after a move is applied is also checked here.",
          "When even one parameter in use is missing (MISSING) or UNKNOWN, it does not guess: it sets state to need_input and leaves points empty (it does not output a partial calculation). Division by zero and out-of-range results are shown for that output only, as DIVISION_BY_ZERO or OUT_OF_RANGE with the location of the node where they occurred; the value is not made 0 or NaN.",
          "A move is checked in the order definition → role → end → conditions → effects; if it is not legal, it returns ILLEGAL_MOVE with the first condition that did not hold (or MOVE_NOT_DEFINED, ROLE_MISMATCH or ENDED). If an effect cannot be applied, it returns EFFECT_FAILED and discards the whole move (next_state is null). A condition or effect that reads beneath an UNKNOWN marker in the state is called neither legal nor a violation: it is made undetermined, and the locations of the markers are listed in need_input.",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only calculation facts. The fields inference and advice are always null, and no string in the output contains wording of judgement. The result is not an approval (result_is_approval: false), and it creates no authority (derives_authority: false).",
          "The results are facts about evaluating the expressions and rules passed: they do not show that a model or rules are correct, they are not a forecast of actual demand, and they are not a judgement of how good a game is. It does not act on anyone's behalf in demand forecasting, game design or rule judgement. Whether the expressions, rules and input values are appropriate is checked by the user.",
          "It holds no specific expressions or game rules in code. Expressions, units, rules and state are all taken from the input.",
          "It does not evaluate the size of the error when 34-digit rounding accumulates node by node. The only signs of numerical instability it outputs are the precision_rounded and OUT_OF_RANGE markers.",
          "The state limits (256 KiB and depth 64) are checked after a move has been fully applied; the size partway through a move is not examined.",
          "It has no clock, no environment locale, no external boundary and no state. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call evaluateModel({ model, inputs, sweep?, seed? }), evaluateRules({ rules, state, role, move?, max_steps? }) and runRules({ rules, state, moves, max_steps }) in src/model-rule.js (they do not throw exceptions). Returns: { ok: true, errors: [], facts } (state is done or need_input, with inference: null and advice: null) or { ok: false, errors: [{ code, path, limit? }] } (path is a JSON Pointer). Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-07",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Evidence, claims and applicability conditions — Evidence, claims and applicability conditions part (Kernel)",
        "summary": "The Evidence, claims and applicability conditions part (Kernel) is a standalone part that returns, for each claim (with a type of fact, testimony, inference or hypothesis, and a source with ref, version and locator), whether its conditions for holding apply to the current conditions, as one of three values: match, mismatch or unknown. A partial match is not made match. When the source's version differs from the current version, the claim is marked stale and its evaluation becomes unknown (SOURCE_CHANGED). Malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "When checking \"can this evidence still be used?\", it prevents treating a claim that matches only some of its conditions as fully applicable, continuing to use an earlier evaluation after the source material has been updated, mixing up \"entered as unknown\" with \"not entered yet\", and promoting an inference or a hypothesis to a fact.",
        "use_when": [
          "You want to compare the conditions (0 to 50, with op eq, ne, in, lt, le, gt or ge) of each claim (0 to 10,000 claims) against the current values in current.conditions and produce applies as match, mismatch or unknown. If any condition is false, the result is mismatch; if none is false but some are unknown, not entered or not comparable, unknown; if all are true, match.",
          "You want to compare the source's version (source.version) against current.source_versions and produce source_status (current, changed, not_entered or unknown) and stale for each claim. When the source is not current, applies is unknown.",
          "You want to distinguish a value entered as { \"unknown\": true } from a value whose key is absent (not entered). Only values not entered are listed in need_input (CONDITION_NOT_ENTERED, SOURCE_VERSION_NOT_ENTERED, SUPPORT_NOT_ENTERED), and state becomes need_input. 0, false and the empty string are treated as values.",
          "You want to keep each claim's type and origin as they are, and receive AI-derived claims (ai_candidate) separately in candidate_claims.",
          "You want the support relations between claims, written with supports, listed for each claim as the shortest typed chain (id, type, origin, depth, chain), and cycles reported through support_cycles and in_support_cycle. Cycles are output, not stopped.",
          "You want to receive, for each claim, why applies became unknown or mismatch, through reasons (in a fixed order) and failed, unknown_keys, missing_keys and not_comparable_keys."
        ],
        "do_not_use_when": [
          "You want it to determine whether a claim is true or whether to adopt it. match is the result of comparing the conditions passed against the current values and does not show that a claim is true. It also does not check whether the content of a claim or the source location (locator) is correct; locator is only returned as it is.",
          "You want to compare conditions using fractional numbers, dates, ranges, string ordering or regular expressions. It compares only equality of strings, integers and booleans, and the ordering of integers (dates are converted to integers by the caller before being passed).",
          "You want to see the ordering of source versions (which one is newer). Old and new are judged only by whether the version strings are equal. The caller checks the current version of a source and passes it; this part does not read the source material.",
          "You want the result of a supporting claim to change the applies of the claim it supports. applies is decided only by the claim's own conditions.",
          "You want every support chain enumerated. It outputs only one shortest chain.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the input is malformed (not plain data, an unknown key, a missing key, a value outside the vocabulary, a wrong type, null, an id that is not 1 to 128 characters, a lone surrogate, the same value twice in a set), it stops with INVALID_INPUT, and when inspecting the values itself throws an exception (a Proxy trap, a revoked Proxy and so on), with INSPECTION_FAILED.",
          "It does not accept getters, class instances, sparse arrays, extra properties on arrays, symbol keys, non-enumerable properties or null. It calculates only on a copy of the values it has read and does not modify the input.",
          "When ids are duplicated, it stops with DUPLICATE_ID, and when a condition operator is none of eq, ne, in, lt, le, gt or ge, with UNSUPPORTED_OPERATOR. It also stops when in has 0 values or 65 or more values, and when the value for lt, le, gt or ge is not a safe integer.",
          "When a limit is exceeded (10,000 claims, 50 conditions in one claim, 64 values for in, 64 supports, 10,000 entries each in current.conditions and source_versions, a total of 2,000,000 ids in the output support chains), it stops with RESOURCE_LIMIT and puts the name of the limit in limit.",
          "Equality is strict, including type (\"5\" and 5, and true and 1, are different). For lt, le, gt and ge, when the current value is not an integer, the condition is not made false but not_comparable, and applies becomes unknown.",
          "When it stops, it returns only the first problem found, as { code, path, limit? } (path is a JSON Pointer).",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null. The result is not an approval (approval: false), and it creates no authority (derives_authority: false).",
          "The results are a presentation of facts about whether conditions apply and about source versions: they are not a judgement of whether a claim is correct, and they do not act on anyone's behalf in deciding whether to adopt it. The truth of a claim is not promised, and it does not recognize any rights. Final authority, blocking and application are the roles of the Host and the Runtime.",
          "Even when a support chain contains a fact, it does not promote an inference or a hypothesis. It does not merge claims either.",
          "It does not decide the meaning of condition keys and values (what counts as holding). The caller decides these and passes them.",
          "The input and output forms are defined by v1 of this part and have not yet been accepted by the user side. Inputs at the full limits were checked only once each in unit tests, and time and memory have not been measured.",
          "It has no clock, no random numbers, no environment locale, no external boundary, no state and no credentials, and it does not use files or network communication. Side effect: NONE.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call evaluateClaims({ claims, current }) in src/evidence-claim.js (it does not throw exceptions and does not modify the input). Input: the list of claims, claims, and the current conditions and source versions, current. Returns: { ok: true, errors: [], facts } (claims, candidate_claims, support_cycles and need_input, with inference: null and advice: null) or { ok: false, errors: [{ code, path, limit? }] } (path is a JSON Pointer). Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-08",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "View model and operation conversion — View model and operation conversion part (Kernel)",
        "summary": "The View model and operation conversion part (Kernel) is a standalone part that, following a registered view contract (view_id, version, entity_kind, fields and commands; declarations only), builds a per-field view model from a common entity (id, version, kind, data) (project), and converts a typed operation event into a typed command candidate (command). Fields absent from the data, unknown values and values of the wrong type are not made empty but output as markers, and violations and missing input are returned as facts. Malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "In the conversion between screens and processing, it prevents mistaking a field absent from the data for an empty value, making a field editable through view settings when it is not editable, mixing scripts or functions into view definitions, and calling AI on every operation.",
        "use_when": [
          "You want to build a per-field view (value, value_state, editable) from entity.data, following the view contract's fields (path, label_key, kind (text, number, enum, list or bool), editable, range, enum, max_length).",
          "You want field values classified by value_state (present, missing, unknown, kind_mismatch or out_of_contract). A field absent from the data is output as { missing: true }, { unknown: true } stays unknown, and a value of the wrong type is output as { kind_mismatch: true }, so they can be told apart from the empty string, 0, false and the empty list.",
          "You want to hide fields, reorder them or make them non-editable with settings (hidden, order, editable). Hidden fields are removed from view and listed in hidden.",
          "You want to convert an operation event (event_type, event_id, path, value) into a typed command candidate (op, path, value, base_version, event_id, event_type), checked against the command definition (op set, increment, select or toggle) selected by the pair (event_type, path). The entity_version passed is copied into base_version.",
          "You want to receive operation violations (UNKNOWN_EVENT, NOT_EDITABLE, VALUE_KIND_MISMATCH, RANGE_VIOLATION) and the need_input of an event whose value is absent or unknown (VALUE_ABSENT, VALUE_UNKNOWN) as facts rather than as ok: false."
        ],
        "do_not_use_when": [
          "You want to render a screen, decide permissions, or apply an operation. This part only converts views and operations. Rendering is the role of the App; permissions, checking against the base version, and applying are the roles of the Host (Runtime).",
          "You want to check whether a view contract is registered, whether its version is correct, or whether the entity's version is the current one. It only copies the contract and versions passed and returns them.",
          "You handle fractional numbers or numbers as decimal strings, or lists of elements other than strings. number is safe integers only, and list is arrays of strings only.",
          "You need a dictionary of display wording or translation. label_key is checked only for its id form, and the part holds no wording (that is the role of the App).",
          "You want to recognize resends of the same event_id. It does not detect duplicates.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the fixed keys, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays, a __proto__ key, symbol keys or null, and checks the values after copying them into plain data.",
          "When there is a function anywhere, or the view contract or settings contain a key that is not declared, it stops with SCRIPT_REFUSED. There is no path that evaluates expressions or scripts.",
          "When the view contract is malformed (a kind or op outside the vocabulary, a mismatched pair of op and kind, min > max, an empty or duplicated enum, max_length out of range, duplicated path or (event_type, path) pairs, a command path that is not a field and so on), it stops with INVALID_CONTRACT. A command constraint allowed that widens the field's enum is also stopped here.",
          "When the entity is malformed, it stops with INVALID_ENTITY; when entity.kind differs from the contract's entity_kind, with ENTITY_KIND_MISMATCH; when the settings are malformed, with INVALID_SETTINGS; when the settings name a path that is not in the contract, with SETTINGS_PATH_UNKNOWN; and when the event is malformed, with INVALID_EVENT.",
          "When the settings try to make editable a field that the contract makes non-editable, it stops with EDITABLE_WIDEN_REFUSED. Differences in view do not increase permissions. command looks only at the contract's editable.",
          "In command, it returns UNKNOWN_EVENT when there is no matching pair, NOT_EDITABLE when the field is non-editable, VALUE_KIND_MISMATCH when the value's type does not match or a toggle has a value, and RANGE_VIOLATION when the value is out of range. A value passes only when it satisfies both the field's constraints and the command's constraints.",
          "When a limit is exceeded (nesting depth 32, 200,000 values, strings of 65,536, arrays of 10,000 elements, 256 fields, 256 commands, 256 each for enum and allowed), it stops with RESOURCE_LIMIT and puts the name of the limit in limit.",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only conversion facts. The fields inference and advice are always null. The result is not an approval (approval: false), and it creates no authority (derives_authority: false). It does not call AI, and it does not use the clock, random numbers, files or network communication.",
          "A command is a candidate, not an application. Applying, checking against the base version, the authenticity of permissions, and conflicts between concurrent updates are the roles of the Host (Runtime).",
          "It does not check whether the value after an increment is applied falls within the field's range. Because it does not hold the base version's value, it limits only the size of the step with the command's range.",
          "out_of_contract values are not hidden but copied and output as they are. kind_mismatch values are not output.",
          "It is not a screen, a rendering engine or a permission mechanism.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call project({ contract, entity, settings? }) and command({ contract, entity_version, event }) in src/view-model.js (neither throws exceptions or modifies the input). Returns: { ok: true, errors: [], facts } (view and hidden for project; command, violation and need_input for command; with inference: null and advice: null) or { ok: false, errors: [{ code, path, limit? }] } (path is a JSON Pointer). Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-09",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Document anchors and fragment editing — Document anchor and fragment editing part (Kernel)",
        "summary": "The Document anchor and fragment editing part (Kernel) is a standalone part that relocates a position (an anchor) in a document divided into blocks within the revised document (exact, relocated, ambiguous or lost), and outputs the diff of a fragment edit and the new block text as candidates. Positions are counted in UTF-16 code units, and the point between the two halves of a surrogate pair is never used as a position. When there are 2 or more candidates, it lists them all and does not choose. An edit that overlaps a protected range is marked PROTECTED_RANGE and no diff is output. The fields inference and advice are always null.",
        "problem": "It prevents the positions of comments and references from drifting when a document is revised. By fixed rules, it does not settle on one position on its own when a position has become ambiguous after a revision, does not create positions that split a surrogate pair such as an emoji, does not make a diff from an edit that touches protected characters, and does not lose the characters that were removed.",
        "use_when": [
          "You want to relocate an anchor made in one version (doc_version, block_id, start, end, quote, prefix, suffix) within the current document (version, blocks) and receive status (exact, relocated, ambiguous or lost) and location (operation: resolve).",
          "You want relocation done in a fixed order of stages: exact (same version, and the characters at the position equal quote) → with context (prefix+quote+suffix) in the same block → with context in all blocks → quote only in the same block → quote only in all blocks. It decides at the first stage that has at least one match.",
          "You do not want an ambiguous position settled. When there are 2 or more matches, it returns ambiguous, sets location to null and lists all candidates (the list holds up to 1,000; candidate_count is the total number, and candidates_truncated shows that the list was cut).",
          "From a fragment edit that replaces [start, end) of a block with replacement, you want to receive the diff (including the removed characters removed and the inserted characters inserted) and new_block_text as candidates (operation: edit). The origin of replacement (registered, human or ai_candidate) is returned as replacement_origin.",
          "You want to stop edits that overlap protected ranges (protected) decided by the caller. On overlap, status is refused, PROTECTED_RANGE is listed in violations, and diff and new_block_text are null."
        ],
        "do_not_use_when": [
          "You want it to determine whether the meaning of the surrounding text is kept after a replacement. It looks only at the protection of characters, and meaning_checked is always false.",
          "You want Unicode normalization (NFC, NFKC), or differences in letter case or whitespace, to be treated as the same, or you want fuzzy relocation of similar text. It compares only code units.",
          "You want grapheme boundaries (combining characters, emoji ZWJ sequences, variation selectors) kept intact. Only surrogate pairs are never split.",
          "You handle anchors or edits that span blocks, or want to follow blocks being split or merged.",
          "You need document saving, version numbering, handling of conflicts in collaborative editing, or keeping the source (which version of which material). This part only returns anchors and versions.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the fixed keys (an unknown key, a missing key, a getter, a non-enumerable property, a symbol key), it stops with INVALID_ARGUMENT; when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED; and when operation is neither resolve nor edit, with UNSUPPORTED_OPERATION.",
          "When the document is malformed (including text containing a lone surrogate), it stops with INVALID_DOCUMENT; when two blocks have the same id, with DUPLICATE_BLOCK_ID; and when the anchor is malformed (start > end, the length of quote is not end − start, prefix or suffix is longer than 32 code units and so on), with INVALID_ANCHOR.",
          "When the edit is malformed, it stops with INVALID_EDIT; when origin is outside the vocabulary, with INVALID_ORIGIN; when a protected range is malformed, empty, in a block not in the document, longer than the block, or between the halves of a surrogate pair, with INVALID_PROTECTED; when the block_id to edit does not exist, with BLOCK_NOT_FOUND; when end exceeds the length of the text, with INVALID_RANGE; and when start or end is between the halves of a surrogate pair, with SURROGATE_SPLIT.",
          "When a limit is exceeded, it stops with RESOURCE_LIMIT and puts one of blocks (10,000), document_bytes (a total of 1,048,576 bytes of text in UTF-8, including the document after an edit) or protected (1,000) in limit.",
          "When the range to remove [start, end) overlaps a protected range by even one character, or an insertion (start = end) is inside a protected range (excluding both ends), it lists PROTECTED_RANGE in violations and outputs no diff (ok is true).",
          "Matches are counted including overlaps (\"aa\" occurs at 2 places in \"aaa\"), and there are no matches that span blocks. The point between the halves of a surrogate pair is used neither in output positions nor in edit or protected positions.",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null, and resolve does not output the document's text. The result is not an approval (approval: false), and it creates no authority (derives_authority: false).",
          "edit only outputs candidates and does not commit or save the document (committed is always false). Which ambiguous candidate to choose and whether to adopt an edit are decided outside this part (by the user of the part or the Runtime).",
          "The results are a presentation of facts about position candidates and edit candidates: they do not settle a position, and keeping the meaning is not promised. It does not act on anyone's behalf in settling positions or editing documents.",
          "It does not create protected ranges. The caller decides what to protect and passes it.",
          "Its form is close to the W3C Web Annotation TextQuoteSelector and TextPositionSelector, but it does not claim to conform to them (positions are in UTF-16 code units, and it has block ids).",
          "It has no clock, no random numbers, no external boundary and no state. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call documentAnchor({ operation: \"resolve\" | \"edit\", ... }) in src/document-anchor.js (resolve and edit can also be called directly; none throws exceptions). resolve takes { document, anchor }, and edit takes { document, block_id, start, end, replacement, origin, protected }. Returns: { ok: true, errors: [], facts } (state is done; inference and advice are null) or { ok: false, errors: [{ code, path }] }. Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-10",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Event aggregation and state reduction — Event aggregation and state reduction part (Kernel)",
        "summary": "The Event aggregation and state reduction part (Kernel) is a standalone part that, with a declaration-only reducer (rules of set, add, append and remove for each event type), applies events in array order to a versioned state, and outputs as facts a candidate new state (new_state) together with the events applied, duplicates, order violations, events outside the window, events that could not be applied, and the count per type. Resends with the same event_id are not applied. Malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "When the current state is built from history, it prevents counts from increasing twice because of resent events, building a state while missing gaps or reversals in seq, rules switching in the middle of a window, a half-finished state remaining where only some of one event's rules were applied, and adding a missing value as 0.",
        "use_when": [
          "You want to apply events (0 to 10,000 of { event_id, seq, at, type, payload }) in order to state with a reducer (version, and for each type 0 to 64 rules { op, path, from (\"payload.<key>\") or value }), and receive the new_state candidate and aggregates.by_type (the count of applied events per type).",
          "You do not want resends with the same event_id applied. If the id is in seen_event_ids, it is listed in duplicates (source: seen); if it appears a second time in the same batch, in duplicates (source: batch); the increase is 0.",
          "You want to check that seq is continuous. Only the previous seq + 1 is accepted; a gap is listed in need_input (SEQ_GAP) and a reversal in order_violations, and in both cases the events after it are not_applied. last_seq can set the first expected seq.",
          "You want to define a window with window (start, end, reducer_version) and mark events outside start ≤ at < end as outside_window (they are not applied, but seq advances).",
          "You want the rules of one event applied all or nothing. If they cannot be applied, everything that event changed is rolled back, event_id, rule_index and code are listed in apply_failures, and the events after it are not_applied."
        ],
        "do_not_use_when": [
          "You want to use it as an event ledger, a delivery platform or a database. Saving, committing and delivery, as well as the duplicate ledger, time, leases and persistence, are the roles of the Host (Runtime).",
          "You want to tell apart events with the same event_id but different content. Duplicates are determined only by event_id, and the content is not compared.",
          "You want events sorted by seq before they are applied. They are applied in array order. When sorting is needed, the caller does it before passing them.",
          "You want to add decimal fractions. add works only on safe integers.",
          "You need set on an array index or replacement within a list. set works only when the parent is an object.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding only the fixed keys, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays, a __proto__ key, symbol keys, null or functions, and applies events after copying the values into plain data.",
          "When the reducer is malformed (an unknown key, an op outside the vocabulary, the form of a path, a __proto__ token, the number of from and value, the form of from, a value for add that is not an integer and so on), it stops with INVALID_REDUCER. The reducer is declarations only, and there is no path that evaluates expressions, scripts or functions.",
          "When state is malformed, it stops with INVALID_STATE; when events are malformed (seq or at is not an integer of 0 or more and so on), with INVALID_EVENTS; when seen_event_ids is not a list of ids, with INVALID_SEEN; and when window is malformed (start > end and so on), with INVALID_WINDOW.",
          "When the window's reducer_version differs from reducer.version, it stops with RULE_SWITCH_IN_WINDOW. Rules are not switched in the middle of a window.",
          "It returns, as a code, why a rule could not be applied: NO_RULE_FOR_TYPE when there is no rule for the type, PAYLOAD_KEY_MISSING when the payload has no such key, PARENT_NOT_OBJECT when the parent is not an object, TARGET_MISSING, TARGET_NOT_INTEGER or TARGET_NOT_LIST when the target is absent, not an integer or not a list, VALUE_NOT_INTEGER when the value is not an integer, and INTEGER_OVERFLOW when the sum overflows. A missing value, an unknown value or a string is not treated as 0.",
          "When a limit is exceeded (nesting depth 32, 400,000 values, strings of 65,536, arrays of 100,000 elements, 10,000 events, 256 types, 64 rules per type, a list of 10,000 after append), it stops with RESOURCE_LIMIT and puts the name of the limit in limit.",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null. The result is not an approval (approval: false), and it creates no authority (derives_authority: false).",
          "new_state is a candidate, not a commitment. Saving, delivery and committing the application are the roles of the Host.",
          "It does not check whether seen_event_ids are events that were actually applied, whether the state's version is the current one, or whether at is a correct time. The Host passes all of these. It does not read the clock and uses only the input values of at and window.",
          "A different event with the same seq (a different event_id) is stopped as a reversal. It does not decide which one is correct.",
          "Equality for remove with a value is judged by the set of keys and matching values (the numbers -0 and 0 are equal).",
          "It has no clock, no random numbers, no files, no network communication, no external boundary and no state. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call reduceEvents({ state, state_version, reducer, events, seen_event_ids, window?, last_seq? }) in src/event-reduce.js (it does not throw exceptions). Returns: { ok: true, errors: [], facts } (new_state, applied, duplicates, order_violations, apply_failures, not_applied, aggregates, need_input and so on; inference and advice are null) or { ok: false, errors: [{ code, path, limit? }] }. Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-11",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Assembly, compatibility and test plan check — Assembly DAG, compatibility and test plan check part (Kernel)",
        "summary": "The Assembly DAG, compatibility and test plan check part (Kernel) is a standalone part that, from a list of parts' ToolLocks (tool_id, contract_version, artifact_digest, ports, capabilities), the connecting edges, the allowed capabilities and draft fixtures, outputs as facts whether the assembly can be connected (connectable), the problems (duplicates, capabilities not allowed, absent ports, mismatched versions or directions, schema or unit mismatches, unconnected required ports, cycles), the edges that need an adapter, and a test plan. Expected values made by AI are not counted as confirmed. The fields inference and advice are always null.",
        "problem": "Inconsistencies in an assembly of parts are not visible until it runs. This part lists, before anything runs, mismatches in the direction, version, schema or unit of connecting edges, required ports that are not connected, cycles, and capabilities that are not allowed. It also prevents counting expected values made by AI as confirmed tests, and answering \"connectable\" when there are no locks.",
        "use_when": [
          "You want to receive connectable and problems from locks (0 to 1,000) and edges (0 to 10,000 of { from: { tool_id, port, contract_version? }, to: the same }). connectable is true only when problems has 0 entries.",
          "You want to check each lock's capabilities against allowed_capabilities and list capabilities that are not allowed as CAPABILITY_NOT_ALLOWED.",
          "You want to know where the schema_digest differs between the two ends of an edge, as the edges that need an adapter (adapters_needed). It does not create adapters.",
          "You want to receive the test plan in the topological order of tools (starting from the smallest tool_id among tools with in-degree 0). Tools in a cycle and beyond it, which cannot be ordered, are listed in unordered.",
          "You want fixtures (id, tool_id, input_digest, expected_digest, origin) divided per tool into confirmed (human, known_source), unconfirmed (ai_candidate) and conflicting. It also lists tools with no confirmed fixture and fixtures for tools that are not in the locks (orphan_fixtures)."
        ],
        "do_not_use_when": [
          "You want to check whether a lock's digest matches the real artifact, signatures, the supplier's authority, or acceptance by the user side. It checks only the form and compares values. These are checked separately, by the Host, the Runtime and so on.",
          "You want to compare the content of schemas or the range of compatibility. If schema_digest is the same, it treats the types as the same; if it differs, it only says that an adapter is needed.",
          "You want it to determine whether units can be converted, or whether different notations mean the same thing. Units are compared only by exact string match.",
          "You want it to determine whether a fixture's expected value is correct, or whether the basis for known_source itself is sound. It only copies origin and sorts by it.",
          "You want 2 or more edges connected to one in port (fan-in), or duplicated identical edges, reported as problems. These are not reported as problems. It also does not determine the old-to-new order of contract_version.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the argument is not a plain object holding exactly the four keys locks, edges, allowed_capabilities and fixtures, it stops with INVALID_ARGUMENT, and when inspecting the values itself throws an exception (a Proxy trap and so on), with INSPECTION_FAILED. It does not accept getters, class instances, sparse arrays, a __proto__ key, symbol keys, null or functions, and checks the values after copying them into plain data.",
          "When a lock is malformed (an unknown key, a digest that is not sha256:<64 lowercase hexadecimal digits>, a direction outside the vocabulary, the form of unit, duplicated port names or capabilities and so on), it stops with INVALID_LOCKS. It does not accept keys such as authorized, and authorized=true does not substitute for a basis.",
          "When an edge is malformed, it stops with INVALID_EDGES; when allowed_capabilities is not a list of ids, with INVALID_CAPABILITIES; and when a fixture is malformed (an origin outside the vocabulary, duplicated ids, an extra key and so on), with INVALID_FIXTURES.",
          "When a limit is exceeded (nesting depth 16, 400,000 values, strings of 4,096, arrays of 100,000 elements, 1,000 locks, 64 ports and 64 capabilities per lock, 1,000 allowed_capabilities, 10,000 edges, 10,000 fixtures), it stops with RESOURCE_LIMIT and puts the name of the limit in limit.",
          "For each end of an edge, it reports PORT_UNKNOWN when the tool or port does not exist, VERSION_MISMATCH when the end's contract_version differs from the lock, and DIRECTION_MISMATCH when the direction is wrong (from is not out, or to is not in). When both ends connect, it reports a difference in schema_digest as SCHEMA_MISMATCH and a difference in units (including unknown units and a unit on only one side) as UNIT_MISMATCH.",
          "It reports locks with the same tool_id as DUPLICATE_TOOL, required ports with no connected edge as REQUIRED_PORT_UNCONNECTED, and strongly connected components of the arrows between tools (2 or more tools, or a self-loop) as CYCLE. When there are 0 locks, connectable is null and need_input (NO_LOCKS) is returned.",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null. The result is not an approval (approval: false), and it creates no authority (derives_authority: false).",
          "The results are a check of facts about an assembly, not permission to adopt, sign, issue or run it. It is not a pack issuer, a signer or a runtime environment.",
          "It does not sign, issue or generate adapters. It does not use the clock, random numbers, files or network communication either.",
          "Expected values from AI (ai_candidate) are listed in unconfirmed and not counted as confirmed. It does not accept majority-vote or confidence fields. When confirmed fixtures have different expected values for the same input, all of those fixtures are placed in conflicting and not counted as confirmed.",
          "The ToolLock fields repository, source_revision, source_path, license, publisher_evidence_ref, consumer_acceptance_ref, input_schema_digest and output_schema_digest are only checked for form and not used.",
          "It has no external boundary and no state. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call checkAssembly({ locks, edges, allowed_capabilities, fixtures }) in src/assembly-check.js (it does not throw exceptions). Returns: { ok: true, errors: [], facts } (connectable, problems, adapters_needed, test_plan, expectation_conflicts, tools_without_confirmed_fixture, orphan_fixtures and need_input; inference and advice are null) or { ok: false, errors: [{ code, path, limit? }] }. Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-12",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Per-role context and items not to use — Per-role context and non-use set part (Kernel)",
        "summary": "The Per-role context and non-use set part (Kernel) is a standalone part that outputs, for each role (with allowed_scopes), the set of items that may be passed to it (projection), the items excluded and the reasons, and the items that are required but not included (missing_required). Exclusion follows derived_from transitively. The transitive non-use set (non_use_closure) is output as candidates for stopping references, and it does not claim that anything was deleted (deleted: false). Malformed input is stopped with a fixed code and location (fail-closed). The fields inference and advice are always null.",
        "problem": "When information that must not be passed to a role is removed, it prevents overlooking items made from items that were set not to be used (derived items), passing items whose origin cannot be checked, and mistaking candidates for stopping references for \"deleted\".",
        "use_when": [
          "You want to receive projection and excluded for each role from roles (0 to 50 of { role, allowed_scopes }), items (0 to 100,000 of { id, scope, derived_from, kind }) and non_use. For each role, projection and excluded divide the items with nothing left out and no overlap.",
          "You want to receive every reason for exclusion in a fixed order. The applicable reasons are listed in reasons in the order NON_USE → SCOPE → DERIVED_FROM_NON_USE → DERIVED_FROM_OUT_OF_SCOPE → DERIVED_FROM_NOT_ENTERED, and the first one is reason.",
          "You want to receive the transitive non-use set as candidates for stopping references. non_use_closure contains the ids in non_use and the items that have an id from non_use among their ancestors; meaning is reference_stop_candidates, and deleted is always false. Ids that are in non_use but not among the items are output in non_use_not_in_items.",
          "You want to pass required (the ids of the items each role needs) and learn, as missing_required, which items are not in projection and why. This is a fact about projection and is not made need_input.",
          "When derived_from names an id that is not in the input, you want that id listed in items_not_entered and need_input (ITEM_NOT_ENTERED). state becomes need_input, and the item stays excluded."
        ],
        "do_not_use_when": [
          "You want it to decide permissions, block information before input, or actually stop references or delete. Final authority is the role of the Runtime; blocking before input and actually stopping references or deleting are the roles of the Host. This part only calculates sets from the lists passed.",
          "You want to check whether allowed_scopes or scope are correct (the authenticity of permissions). The caller decides the vocabulary of scopes and passes it; this part does not check it.",
          "You want to handle the content of a policy or differences from an earlier policy. policy_version is only checked for form and copied.",
          "You want different handling for each item type (kind), or want to know which ancestor caused an exclusion. kind is not used in the calculation, and only the type of reason is output.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "When the input is malformed (not plain data, an unknown key, a missing key, a value outside the vocabulary, a wrong type, null, an id that is not 1 to 128 characters, a lone surrogate, the same value twice in a set), it stops with INVALID_INPUT, and when inspecting the values itself throws an exception (a Proxy trap, a revoked Proxy and so on), with INSPECTION_FAILED.",
          "It does not accept getters, class instances, sparse arrays, extra properties on arrays, symbol keys, non-enumerable properties or null. It calculates only on a copy of the values it has read and does not modify the input.",
          "When role names or item ids are duplicated, it stops with DUPLICATE_ID; when required names a role that is not in roles, with UNKNOWN_ROLE; and when derived_from forms a cycle (including an item that names itself), with DERIVATION_CYCLE. The location of a cycle is the item on the cycle with the smallest index (/items/<index>).",
          "When a limit is exceeded (50 roles, 1,000 allowed_scopes, 100,000 items, 1,000 derived_from entries in one item, a total of 1,000,000 derived_from entries, 100,000 non_use entries, 50 roles in required and 100,000 ids for one role), it stops with RESOURCE_LIMIT and puts the name of the limit in limit.",
          "Exclusion follows ancestors of ancestors. Items with an id among their ancestors that is not in the input (items that cannot be checked) are not put in projection.",
          "When it stops, it returns only the first problem found, as { code, path, limit? } (path is a JSON Pointer).",
          "Fuzzing that compares the results against an independent oracle written without using the helper functions in src confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only facts. The fields inference and advice are always null. The result is not an approval (approval: false), and it creates no authority (derives_authority: false).",
          "It deletes nothing. non_use_closure is a set of candidates for stopping references, not a record of deletion. Actually stopping references, deletion and deletion failures happen outside this part.",
          "It does not grant permissions, block, or delete on anyone's behalf. Final authority, blocking and application are the roles of the Host and the Runtime.",
          "It does not save non-use records or reference stops (it has no state).",
          "The input and output forms are defined by v1 of this part and have not yet been accepted by the user side. Inputs at the full limits were checked only once each in unit tests, and time and memory have not been measured.",
          "It has no clock, no random numbers, no external boundary and no credentials, and it does not use files or network communication. Side effect: NONE.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Call projectRoleContext({ roles, items, non_use, policy_version, required? }) in src/role-context.js (it does not throw exceptions). Returns: { ok: true, errors: [], facts } (projection, excluded and missing_required for each role, and non_use_closure, non_use_not_in_items, items_not_entered and need_input; inference and advice are null) or { ok: false, errors: [{ code, path, limit? }] }. Side effect: NONE. There is no state, no external connection and no credentials, and there is no port for the user to provide.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-13",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Content, revision and patch application — Content, Revision and Patch Application Tsugite",
        "summary": "The Content, Revision and Patch Application Tsugite (継手, tsugite) carries a Kernel's typed patch (typed-patch/v1) to one piece of content in a Runtime store, either to apply it (APPLY) under an optimistic revision (expected_revision) or to check it without writing (CHECK), and checks that the returned receipt (ContentPatchApplyReceipt) follows the contract. The store belongs to the Runtime; this Tsugite does not create a store (fail-closed).",
        "problem": "Behind saving, it prevents one person's change from overwriting someone else's. It carries the patch bound to the revision and to the user's confirmation, does not confuse received, applied and unknown, and does not apply a patch twice.",
        "use_when": [
          "You want to apply a change to content only when the base revision (expected_revision) is the same as the current revision. If the revision differs, the result is CONFLICT and nothing is written.",
          "You want to check only whether a patch can be applied, without writing it (op CHECK, effect NONE).",
          "You want to carry an AI proposal as adopted (ADOPTED_CANDIDATE) only after the user has personally checked that proposal and its patch. The confirmation is received through client.recordConfirmation.",
          "You want to resolve an apply whose result is unknown by resending the same request, so that the patch is not applied twice."
        ],
        "do_not_use_when": [
          "You want the store or revision management itself. Atomic application, revisions and permissions are the Runtime's role; this Tsugite does not create a store.",
          "You want to create a patch, or to check the correctness of a patch's content or of its inverse operation. That is the role of the Kernel and the Runtime.",
          "You want an automatic rebase or retry after CONFLICT. This Tsugite does not silently rebase or retry.",
          "You want a connection target you can use right away. It has applied to a real Runtime zero times, and the port is only a test stand-in. The Runtime-side commands needed to implement the port exist only as a design so far.",
          "You want to build business rules. This Tsugite is an application-platform Tsugite that connects Kernels with data and services; business rules are handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the contract version. When a request's contract_pin differs from the configured contract_schema_digest, it does not send it. Without a configuration, it sends nothing.",
          "It sends only to workspaces allowed by the configured workspace_ids. Knowing a content hash is not treated as a permission.",
          "It checks the user's confirmation. It sends ADOPTED_CANDIDATE only when there is a confirmation by the user personally (origin HUMAN) bound to that proposal and its patch (the patch_digest value). A confirmation is used up by an APPLY answer that has been matched.",
          "It matches what was received. It accepts a receipt only when the received patch_digest is exactly the same as in the request and base_revision equals expected_revision. It accepts APPLIED only when new_revision is expected_revision + 1 and there is a digest of the new content, and CONFLICT only when nothing has been written.",
          "It accepts a receipt as an ordinary answer only when its contract_pin is the same as the request's. For a different version it accepts only an INCOMPATIBLE_VERSION refusal; an INCOMPATIBLE_VERSION with the same pin is treated as an anomaly and it stops.",
          "An APPLY whose result is unknown (a port failure, or an UNKNOWN receipt) is resolved by resending the same request (if port.lookup exists, it reads the record first). Until it is resolved, it sends no other request, and it never sends different content under the same request_id.",
          "It does not accept, and stops on, a receipt that claims to be an ERABAZU store, a receipt whose effect is escalated or unknown, or a receipt for a different request or a different workspace (HALTED after RECEIPT_REJECTED). The effect of APPLY is LOCAL_REVERSIBLE; the effect of CHECK is NONE."
        ],
        "what_it_does_not_do": [
          "It does not check that the patch bytes hash to the patch_digest value, that target.content_digest is the digest of the base revision's content, or that the new content digest is the digest of the content after application (that is the Runtime's role).",
          "It does not decide on permissions (REF_NOT_PERMITTED), a different workspace (WORKSPACE_MISMATCH) or protected ranges (PROTECTED_RANGE). The Runtime decides, and this Tsugite returns that answer.",
          "Not applying a patch twice depends on the Runtime's deduplication. The tests only checked that the stand-in behaves that way.",
          "The records of user confirmations and of matched receipts exist only inside this Tsugite and are lost on restart. It does not check whether a port that claims a different version really has that version.",
          "Being applied is not approval: it does not mean the content is correct, and it is not approval to adopt (every result carries approval: false).",
          "It does not change the contract. The schema authority for the contract has not been set yet (NOT_SPECIFIED).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/content-patch-apply/v1 (ContentPatchApplyRequest / ContentPatchApplyReceipt). submit(request) on createContentPatchApplyClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (always approval: false). User provides: a port to the Runtime (submit, and optionally lookup) and cfg (contract_schema_digest and workspace_ids). Side effect: APPLY is LOCAL_REVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-14",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "File input and output — File Input/Output Tsugite",
        "summary": "The File Input/Output Tsugite (継手, tsugite) uses the handle of a file the user selected in the App to carry requests (FileIoRequest) for an import preview (IMPORT_PREVIEW) and an export (EXPORT) of CSV (RFC 4180, UTF-8) and JSON to the Host, and matches the returned receipt (FileIoReceipt) against the contract. It does not treat a preview as adoption, and it reads back the exported bytes itself to check them (fail-closed).",
        "problem": "It prevents reading or writing a path or URL the user did not select, mistaking an import preview for adoption, and exporting a CSV that still contains cells that look like formulas (cells starting with = + - @ TAB CR).",
        "use_when": [
          "You want to preview a CSV or JSON file the user selected before importing it. The preview returns counts, issues with only a location and a code, and digests.",
          "You want to export CSV or JSON as a new file and have the Tsugite read it back to check the written bytes' length, sha256, cell quoting and formula protection.",
          "You want to limit the ops, formats, limits and mappings handled to the range pinned in the configuration.",
          "You want an export whose result is unknown not to be resent automatically, and further exports to stop until the App has checked the output."
        ],
        "do_not_use_when": [
          "You want to adopt (apply) the import result as it is. A preview is not adoption (adopted: false); adoption is the role of the Content, Revision and Patch Application Tsugite.",
          "You want to check the correctness of the conversion, of the counts or of the issue locations. That is the role of the conversion and matching parts (Kernels) and of the App.",
          "You want to handle formats other than CSV (RFC 4180) and JSON, or character encodings other than UTF-8.",
          "You want to overwrite an existing file. Exports go to new files only.",
          "You want to read or write by specifying a path or URL directly. This Tsugite uses only handles the App passed through admitHandle.",
          "You want a Host you can use right away. It has never connected to a real Host or file; the port implementation, how handles are issued and the configuration values will be settled at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the configuration. It does not send to the Host a request not in allowed_ops or allowed_media_types, a request exceeding the limit ceiling (limits_ceiling; at most the contract's 50 MiB, 1,000,000 rows and 1024 columns), an unpinned mapping, or a request whose declared extension does not match the format (such as a CSV named .json). An empty list allows nothing.",
          "It checks the contract version. If a request's contract_pin differs from the configured contract_schema_digest, it does not send it. If the version a receipt claims differs from the request's, it accepts nothing except a REJECTED with INCOMPATIBLE_VERSION.",
          "It checks handles. It does not use a handle the App has not passed, a handle used in the wrong direction (such as an import source handle used for output), a URL or Windows path, or an output handle that has already been exported to.",
          "It checks the shape of requests. It does not send a CSV without a dialect, a JSON with a dialect or formula protection, a CSV export without formula protection, mixed op fields, an export with key_fields, or an export that allows overwriting. It also does not send a different request under the same request_id (REFUSED).",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a receipt for a different op, a receipt claiming the source was changed, an escalated or unknown effect, a reason that does not fit the op, a receipt whose counts do not add up (rows_valid + rows_invalid is not rows_read), or issues that contain cell values or source text.",
          "It checks the location and order of issues. It does not accept a receipt that does not fit the row range (0 to rows_read for CSV, 1 to rows_read for JSON), the location field for each format (column for CSV, json_pointer for JSON), whether each code has a location, or the source order (within the same row, a whole-row issue comes first).",
          "After an export, the Tsugite itself reads the bytes the Host read back (readOutput). It does not accept the result when the length or sha256 differs from the receipt, the bytes are not UTF-8, a CSV cell is not quoted, a cell that looks like a formula remains, the header is not exactly one line, the JSON is not a single array of objects, the row or cell counts do not match, or the receipt overstates the number of escapes added (cells_escaped).",
          "After a Host failure, UNKNOWN, FAILED or an output that cannot be read back, it does not resend the export automatically. It only asks through lookup, and it does not export again until the App calls outputChecked (imports only read, so they can continue)."
        ],
        "what_it_does_not_do": [
          "A preview is not adoption, and a successful export does not mean the content is correct (every result carries approval: false).",
          "It does not read the import bytes (the Host reads them). It checks only the shape and target of source_digest and preview_digest and does not recompute them. It does not check the correctness of the conversion, of the issue locations or of the counts.",
          "Detecting size, leading bytes and extension, UTF-8 and duplicate JSON keys is the Host's role. The Tsugite checks only the shape of the receipt and the combination of reasons.",
          "Checking the written bytes assumes the bytes the Host passed are the same as the real file. For cells_escaped it checks only the upper bound. It does not check whether CSV headers or JSON keys are the mapping's names, or the value types.",
          "Formula protection covers only the contract's triggers (= + - @ TAB CR). A formula after leading spaces, full-width symbols, and other interpretations specific to each spreadsheet application (such as DDE) are out of scope.",
          "Handle state and exports whose result is unknown exist only inside this Tsugite and are lost on restart. outputChecked only receives the App's statement; it does not check whether the App really checked. It also does not check whether a port that claims a different version really has that version.",
          "It is not a template for using a specific spreadsheet application or file-picker mechanism.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/file-io/v1 (FileIoRequest / FileIoReceipt). On createFileIoClient({ cfg, port }), the App passes handles through admitHandle, and submit(request) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). User provides: a port to the Host (submit, optionally lookup, and readOutput for exports) and the pins in cfg (version, ops, formats, limits and mappings). Side effect: NONE for a preview, LOCAL_REVERSIBLE for an export.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-15",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "AI execution connection — AI Execution Connection Tsugite",
        "summary": "The AI Execution Connection Tsugite (継手, tsugite) carries one AI call fixed by the Runtime (operation, slot, permitted context, budget reservation and attempt number) to the AI service as a request (AiExecutionConnectRequest), and matches the returned receipt (AiExecutionConnectReceipt). It matches delivery state, usage and settlement separately, never turns an unknown into 0, and does not resend automatically (fail-closed).",
        "problem": "When an AI call ends in a timeout or a lost connection, it prevents releasing the reservation with usage recorded as 0 although delivery is unknown, and prevents calling twice by simply resending. By matching, it brings to light mismatches between the records of usage and of results, which are hard to see behind the call.",
        "use_when": [
          "You want to carry one AI call to the AI service using only a provider and model pair the deployment allows and only permitted context references (up to 32).",
          "You want a timeout or a lost connection to be received as: delivery unknown (UNKNOWN_DELIVERY), usage UNKNOWN, reservation still held, retries stopped.",
          "You want to issue a resend only as a new attempt after the previous attempt is known to be FAILED_CONFIRMED or NOT_EXECUTED_PROVEN. A new attempt uses a new request_id, operation_id and reservation, from the same budget, up to 3 attempts per slot.",
          "You want to bind the digest of the AI service's capability declaration and the transport limit (transport_limit_ms) to the request, and stop a call that does not match the declaration before it is sent.",
          "You want to accept a COMPLETED reply only after checking its byte size and sha256."
        ],
        "do_not_use_when": [
          "You want to call a provider directly. Calling the provider is the AI service's role; this Tsugite is the Runtime-side half.",
          "You want to call AI from the App or from a part (Kernel). Only the Runtime can call this Tsugite.",
          "You want an automatic resend after a failure. This Tsugite does not resend automatically; the Runtime decides on a new attempt.",
          "You want to delegate budget, planning or matching decisions. Those are the Runtime's role. The Runtime decides the result of matching (verdict); this Tsugite only records it.",
          "You want a connection to an AI service you can use right away. It has never connected to a real AI service or provider (zero calls); the port implementation and configuration values will be settled at a separate gate when connecting.",
          "You want a template for using a specific AI provider's SDK or billing API. This Tsugite is not such a template."
        ],
        "what_it_checks": [
          "It checks the caller and the configuration. It does not send a call that does not come from the Runtime, a provider and model the deployment does not allow, a currency not in the configuration, or a request whose contract version differs from the one fixed for the deployment. An empty table allows nothing.",
          "It checks the shape of the request. It does not send a request with a raw key field, 33 or more or duplicated context references, a fractional amount of money, a max_output_tokens greater than the reserved tokens, a transport_limit_ms that is missing or outside the range (an integer from 1,000 to 600,000), a declaration digest that is missing or points to a different domain or a different provider, or an ai_service_capability_read_at later than the request's time.",
          "It checks the provider-side document (ai-execution-request/v1). It does not send a document whose sha256(canonical) differs from provider_request_digest, whose provider or model differs from the request, or that contains unpermitted references, raw body text, retry_profile, values that look like actual keys, secret field names, or an auth_ref that is not in reference form.",
          "It checks the order of attempts. It does not send a 4th attempt in a slot, a 2nd attempt without a previous attempt, a prior_verdict that does not match the previous attempt's result, a retry before matching or after RESPONSE_DURABLE, or an attempt with a different budget, a used reservation, a used operation_id, a different slot, or a digest that does not match the previous receipt.",
          "When the declaration document (the document from declareCapabilities()) is attached, it compares whether its sha256(canonical) equals the request's digest and whether its transport_limit_ms field equals the request's limit. A declaration that does not fit the canonical form (fractions, integers outside the safe integer range, arrays with holes, getters and so on) is refused without comparison.",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a different operation or reservation, a receipt that sets usage to 0, releases the reservation, allows a retry or claims delivery despite a timeout, a COMPLETED without the provider's receipt or a reply, a SETTLED without grounds, or a receipt claiming that prompts or keys were written to logs.",
          "It matches the version and origin of receipts. It does not accept a receipt with no pin or a different version, an INCOMPATIBLE_VERSION with the same pin as the request, a receipt in which the AI service claims to be the Runtime's record, or a receipt that uses the same provider receipt for a different operation (RECEIPT_CONFLICT). After CAPABILITY_MISMATCH, it sends an attempt only when its digest equals that receipt's current_capability_digest.",
          "For COMPLETED, it reads the reply bytes with readReply and accepts them only when the length is byte_size (up to 16,384) and the sha256 equals reply_digest. If they cannot be read, the result is UNKNOWN; if they do not match, it does not accept them and stops."
        ],
        "what_it_does_not_do": [
          "Delivery does not mean the content is correct or adopted. Every result carries approval: false and automatic_re_request: false, and the Runtime decides on a re-request as a new attempt.",
          "It does not check the meaning, correctness or adoption of the reply's content (only delivery and the sha256 of the bytes). It also does not check the correctness of the matching result (verdict).",
          "It does not check the usage values, unit rates or the correctness of settlement, or whether the reserved amount is enough. Usage that exceeds the reservation is also returned as a fact, as it is.",
          "It does not handle keys. Keys are held by the AI service and referred to by reference (HELD_BY_PORT). It also does not copy the message of a port exception into the result, because it may contain an actual key.",
          "Attempt records, reservation IDs, operation_ids and the provider receipts it has seen exist only inside this Tsugite and are lost on restart. After a restart, it does not send attempts from the 2nd onward.",
          "It does not decide when to give up on an attempt with no answer and turn it into a Runtime record (recordNoAnswer). This Tsugite has no clock; it only checks that the record's time is at least the request's time + transport_limit_ms.",
          "It does not check whether an attached declaration document or current_capability_digest really belongs to the AI service's current declaration, or whether the Runtime really re-read the declaration. Whether a real AI service refuses with CAPABILITY_MISMATCH was tried only with a stand-in.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/ai-execution-connect/v1. submit(request, providerRequest, declaration?) on createAiExecutionConnectClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). The Runtime records through recordNoAnswer and recordReconciliation. User provides: a port to the AI service (submit and readReply, and optionally lookup) and the pins in cfg. Side effect: a request is EXTERNAL_IRREVERSIBLE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-16",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Display and playback connection — Display and Playback Connection Tsugite",
        "summary": "The Display and Playback Connection Tsugite (継手, tsugite) passes a view model produced by a part (Kernel) to the App's screen, together with assets and playback settings fixed by digest (ViewPlaybackRequest), and matches the receipt the App returns (ViewPlaybackReceipt). The App draws; this Tsugite does not draw. Whether something was shown or played is checked against the App's build and the output digest (fail-closed).",
        "problem": "It prevents believing \"displayed\" or \"played\" without evidence, turning the same user action into a command twice, and mistaking showing something for a change of permissions.",
        "use_when": [
          "You want to pass a view model, assets and playback settings fixed by digest to the App's screen, and check what was shown against the App's build and the output digest.",
          "You want to check that the same input (op, view model, view contract, assets, playback settings and App build) gives the same output digest (reproducibility).",
          "You want to receive user actions as typed commands only once per view (view_session_id) and per operation_id.",
          "You want missing assets reported with a reason (NOT_FOUND when absent from the asset storage, DIGEST_MISMATCH when the digest differs) and the observed digest."
        ],
        "do_not_use_when": [
          "You want the drawing itself. The App draws; this Tsugite does not draw.",
          "You want to generate images. Image generation is outside this Tsugite's scope.",
          "You want to apply typed commands. This Tsugite does not send commands. Applying them is the role of the Content, Revision and Patch Application Tsugite or of the Runtime.",
          "You want to use media types other than image, audio, video, text and font, such as scripts, as assets.",
          "You want a screen or asset storage you can use right away. It has never connected to a real App, screen or asset storage (zero drawings and playbacks); the port implementation and configuration values will be settled at a separate gate when connecting.",
          "You want a template for a specific UI framework, player or rendering engine. This Tsugite is not such a template."
        ],
        "what_it_checks": [
          "It checks the configuration and the request. It does not send a contract version different from the one fixed for the deployment, a view contract, version or media type not in the configuration (view_contracts, media_types), a script media type, 257 or more assets, a request that lists the same asset_ref twice, or a request that declares a side effect. An empty table allows nothing.",
          "It matches what was shown. It does not accept a receipt that answers RENDER with PLAY (or the reverse), a receipt with no evidence of showing, a view model or settings digest different from the request, or a receipt whose output digest changed for the same input.",
          "It matches missing assets. It does not accept an ASSET_MISSING without a list, assets or media types not in the request, a missing asset without a reason, a DIGEST_MISMATCH with no observed digest or with the same value as declared, a NOT_FOUND with a digest, or a receipt that reports the same asset as missing twice.",
          "It matches actions. It does not accept a receipt that fails to list a duplicate operation_id, calls a new action a duplicate, has actions for a view that was not shown, has playback actions for RENDER, or has 1,001 or more events or events of an unknown kind. Only events with a new operation_id become commands.",
          "It matches the receipt's declarations. It does not accept a receipt claiming that it generated an image, added UI to the Runtime, granted permissions or called AI.",
          "It matches the version and correlation. It does not accept, and stops on (HALTED), a spoofed request_digest, a receipt with no pin or a different version, an INCOMPATIBLE_VERSION with the same pin as the request, a receipt for a different view (view_session_id), or a digest with a different target."
        ],
        "what_it_does_not_do": [
          "Showing something allows nothing. Every result carries approval: false and permission_change: NONE.",
          "It does not check what was actually visible on the screen (pixels and sound). The output digest is the digest of the App's deterministic description of the drawing, and its correctness is the App's responsibility.",
          "It does not hold the asset bytes. Checking the bytes (NOT_FOUND, DIGEST_MISMATCH) is the App's role, and it cannot check whether the App listed every missing asset or whether an observed digest is really the observed value (it only offers checkAssetBytes for the App's own check).",
          "It does not check the correctness of the view model itself, the content of the view contract or the content of typed commands (only digests and refs).",
          "View records (operation_ids seen and reproducibility keys) exist only inside this Tsugite and are lost on restart. Duplicates after a restart cannot be counted.",
          "It does not accept receipts pushed from the App. It matches only the receipt that answers submit, and assumes the App answers a resend with the same request_id with the same receipt.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/view-playback/v1. submit(request) on createViewPlaybackClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false and permission_change: NONE). DONE carries output_digest, missing_assets, commands (typed commands) and more. User provides: a port to the App's screen (submit, and optionally lookup) and cfg (version, view contracts and media types). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-17",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Shared revisions with multiple participants — Multi-Participant Shared Revision Tsugite",
        "summary": "The Multi-Participant Shared Revision Tsugite (継手, tsugite) carries participant events (proposing a change PROPOSE_CHANGE, consent CONSENT, withdrawing consent WITHDRAW_CONSENT, leaving LEAVE, synchronizing SYNC) one at a time to the shared Host (SharedRevisionRequest), and matches the receipt (SharedRevisionReceipt). It binds consent to (who, which revision, that revision's digest) and does not let it carry over to the next revision (fail-closed).",
        "problem": "In collaborative editing, it prevents counting consent to an old revision as consent to a new one, counting an AI role as a person participant, and reissuing an event whose result is unknown after a disconnection so that it is applied twice.",
        "use_when": [
          "You want to carry a flow in which several participants propose changes in a shared space, consent, withdraw consent and leave, one event at a time as the contract requires.",
          "You want to bind consent to (who, revision, digest) and check that, once a change goes through, consent to the old revision is reported as invalidated.",
          "You want an AI role to take part only as something operated by a person whose authentication has been recorded, without letting it consent, withdraw or leave, and without counting it as a person.",
          "You want to resolve an event whose result is unknown after a disconnection by resending it with the same event_id."
        ],
        "do_not_use_when": [
          "You want to run a shared space right now. Who will operate the shared Host has not been decided, and the Host itself does not exist. Until it is decided, every event is refused with HOST_NOT_ASSIGNED and nothing is applied.",
          "You want a shared Host, a collaborative editing service or a consent management service itself. This Tsugite does not create a Host.",
          "You want to apply patches to the shared content. That is the role of the Content, Revision and Patch Application Tsugite (P-13). Ordering and applying events, and holding revisions, consents and deduplication, is the shared Host's role.",
          "You want participants' identities verified. This Tsugite only carries refs to authentication evidence; identity verification is the Host's role.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the configuration and references. It does not send a request when there is no configuration, the contract pin differs from contract_schema_digest, the host_ref differs from the configuration, the workspace is not allowed (workspace_ids), a reference contains .., a drive, control characters or U+2028, the op is unknown, there are extra fields, or the body or intent does not fit the op.",
          "It checks participants. It does not send events from a person whose authentication evidence has not been recorded, an unregistered AI role, an AI role with a different operator or one that operates itself, a request that uses the same reference both as a person and as an AI role, or a participant who has left (and the AI roles that person operates).",
          "It does not send consent, withdrawal or leaving by an AI role. It does not accept a receipt claiming that an AI's consent was accepted, or an invalidation or leave notice that names an AI role.",
          "It matches revisions and consent. It does not accept a change whose new revision is not the base revision + 1, an event that advances the revision without being a change, a revision that goes back before one already known, a CONFLICT at the base revision, a consent record whose (who, revision, digest) differs from the request, consent carried over, or an invalidation without a notice. Consent is sent only to the base revision itself.",
          "It matches answers to withdrawal and leaving. It does not accept a withdrawal that does not list the ended consent, an answer that lists consent by another person or to a revision that is not current, a leave that omits that person's known consents, a leave without the invalidated_consents field, or CONSENT_NOT_FOUND for an op that is not a withdrawal.",
          "It checks resends. It does not send a request with different content under the same event_id or the same request_id, or a request that reissues an event under a new event_id while an event with an unknown result exists. It does not accept DUPLICATE_EVENT for a new event or EVENT_ID_REUSE for a resend.",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a different op or event, a state outside the op's table, a receipt that escalates the effect, a receipt with no pin or a different version, or a receipt in which a database in a shared folder or an encrypted blob claims to be the shared Host."
        ],
        "what_it_does_not_do": [
          "Being accepted or having consent recorded does not mean the content is correct, nor is it the consent of other participants (every result carries approval: false).",
          "It does not check whether the shared content (the content of change_ref) and the state digest (target_digest) are correct.",
          "It only carries refs to authentication evidence. It does not perform identity verification itself, and does not check whether the person operating an AI role really is that person.",
          "Participant, consent and revision records exist only inside this Tsugite and are lost on restart. It learns about consent, withdrawal or leaving done on other devices only through notices or the Host's answers. It does not check whether the Host delivered notices to all other participants.",
          "It has never connected to a real shared Host. The tests used only a local stand-in and the HOST_NOT_ASSIGNED port, and the order of checks (pin, then Host assignment, then participants and events) was checked only with the stand-in.",
          "It checks only the shape of observed_timestamp and does not check whether the clock is correct. It also does not check whether the answering port really has that version.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/shared-revision/v1. On createSharedRevisionClient({ cfg, port }), recordAuthentication and registerAiRole record people and AI roles, and submit(request) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). User provides: a port to the shared Host (createHostNotAssignedPort while the operator is undecided) and cfg (version, host_ref and workspace_ids). Side effect: NONE for SYNC, EXTERNAL_REVERSIBLE for the others.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-18",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Limited operation and observation connection — Limited Operation and Observation Connection Tsugite",
        "summary": "The Limited Operation and Observation Connection Tsugite (継手, tsugite) carries one step of a trial (observing OBSERVE, one permitted action ACT, or resetting to the initial snapshot RESET) to the operation Host of the App's sandbox (SandboxOperationRequest), and matches the receipt (SandboxOperationReceipt). Its scope is only inside the sandbox; it lets through no request or receipt that claims sending outside, purchasing or broad desktop permissions (fail-closed).",
        "problem": "It prevents trial operations from leaking outside the sandbox, filling in an action whose result is unknown by clicking again and so acting twice, and continuing to the next trial in an environment that could not be restored.",
        "use_when": [
          "You want to carry observation, permitted actions and resets to the initial state in a trial environment (sandbox), one step at a time, as the contract requires.",
          "You want to send an action only when it is within the pinned action set and based on the latest observation in that trial (one not yet used as the basis of an action).",
          "You want each trial to start with a reset whose read-back snapshot equals the initial one (RESET_VERIFIED), and the sequence to advance from 0 by 1.",
          "You want nothing to be sent to a sandbox after a failed reset until it is recorded that a person has restored it (recordHumanRestore)."
        ],
        "do_not_use_when": [
          "You want to send to real sites, make purchases or operate a broad desktop. This Tsugite's scope is only inside the sandbox.",
          "You want to delegate observation, actions and resets themselves, time measurement, or sandbox isolation. Those are the role of the App's sandbox and the operation Host; this Tsugite does not operate anything.",
          "You want to delegate deciding whether the goal was reached (the goal check). The goal check is the operation Host's role.",
          "You want a template for a screen-automation tool or for browser automation. This Tsugite is not such a template.",
          "You want a sandbox you can use right away. It has never connected to a real App sandbox or operation Host; the tests used only a local stand-in (a three-page practice app).",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks the configuration and scope. It does not send a request when there is no configuration, the contract pin differs from contract_schema_digest, the sandbox is not allowed, the action set is unpinned or has a different digest, the target, action type or selector is outside the set, the request claims sending outside the sandbox, purchasing or a broad desktop, or the limits are out of range (max_actions 1 to 200, max_seconds 1 to 3600).",
          "It checks the shape of requests. It does not send a request with a reference containing .., a drive or control characters, an unknown op, extra fields, an intent that does not fit the op, an unknown action type, a TYPE_TEXT without input, or a digest with a different target.",
          "It checks the order of trials. It does not send a trial that does not start with RESET, a trial_id already used, a stopped sandbox or an ended trial, a sequence that skips or repeats, or a trial whose conditions change midway. When max_actions is reached, it does not send ACT.",
          "It checks actions. It does not send an ACT without an observation or based on an old observation, a second ACT on one observation, a resend of the same action_id (clicking again), or an ACT or RESET after ACTION_UNKNOWN. After ACTION_UNKNOWN, it sends only OBSERVE.",
          "It matches resets. It does not accept a RESET_VERIFIED whose snapshot differs from the initial one, a RESET_FAILED that allows the next trial, or a RESET_FAILED whose snapshot equals the initial one.",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a different op, trial or sequence, a receipt claiming that something was sent outside or purchased, a receipt that escalates the effect, a reach result without a goal, an ACTION_PERFORMED without a digest of the action record, an actions_used that is miscounted or over the limit, a refusal reason not in the op's table, or a receipt with no pin or a different version.",
          "It does not resend a request whose result is unknown because of a port failure. It only reads the record through lookup; if nothing is found, OBSERVE and ACT end that trial, and RESET is treated as a failure, stopping the sandbox until a person restores it."
        ],
        "what_it_does_not_do": [
          "Having acted or reached a goal does not mean the action was correct or that the user adopted it (every result carries approval: false).",
          "It does not check sandbox isolation, purchase prevention or screen permissions. It only matches the receipt's zero outside sends and zero purchases; whether they were actually prevented is the operation Host's role.",
          "It does not check whether the content and digests of observations and snapshots match the screen, whether the goal check is correct, or whether an action record really belongs to that action.",
          "It has no clock and does not time max_seconds. It learns of a timeout from the operation Host's LIMIT_REACHED. It also does not read the action set document itself; it only matches against the configured set and the digest value.",
          "Trial records exist only inside this Tsugite and are lost on restart. It learns that a person restored a sandbox only through an evidence ref, and checks whether it was restored by the read-back at the next reset.",
          "For SEQUENCE_GAP refusals and TARGET_NOT_PERMITTED refusals of OBSERVE, it takes the Host's statement as it is and does not check the cause of the discrepancy. It also does not check whether the answering port really has that version.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/sandbox-operation/v1. submit(request) on createSandboxOperationClient({ cfg, port }) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). For a stopped sandbox, recordHumanRestore records that a person restored it. User provides: a port to the operation Host (submit, and optionally lookup) and cfg (version, sandbox_refs and action_sets). Side effect: NONE for OBSERVE, LOCAL_REVERSIBLE for ACT and RESET.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-19",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Material and asset reference — Material and Asset Reference Tsugite",
        "summary": "The Material and Asset Reference Tsugite (継手, tsugite) fixes one location in a local material the user designated, by revision (exact_revision) or file digest, asks the material Host for a reference with terms of use and a role scope attached (MaterialReferenceRequest), and matches the returned receipt (MaterialReferenceReceipt) against the contract. It either references the fixed material or returns nothing (fail-closed).",
        "problem": "It prevents a referenced material from being replaced midway and the new revision being used without anyone noticing. It also prevents treating instruction-like text inside a material as a command to execute, and filling in an unknown source by guessing.",
        "use_when": [
          "You want to reference a local material the user designated, fixed by revision or digest, with terms of use.",
          "When the original has changed, you want to be told by REVISION_CHANGED with the observed revision and digest, instead of moving silently to the new revision.",
          "You want to receive only a set location in a material (a range of pages, lines, characters, time and so on) as bytes whose sha256 and size have been checked.",
          "You want permission revoked (PERMISSION_REVOKED), a broken reference (NOT_FOUND) and a missing location (LOCATOR_UNRESOLVED) received as separate states."
        ],
        "do_not_use_when": [
          "You want to reference external material such as a URL or another service. External material is out of scope and needs a separate capability declaration and permission.",
          "You want to check whether a material's content is correct or factual. This Tsugite does not interpret or check the content (NOT_VERIFIED).",
          "When the original changes, you want it re-referenced automatically at the new revision. It does not create a request with a new fix; the user or the Runtime decides.",
          "You want a template for using a specific document management or search service. This Tsugite is not such a template.",
          "You want a material Host you can use right away. It has never connected to a real material or material Host (zero real references); the port implementation, the mechanism for designating materials and the configuration values will be settled at a separate gate when connecting.",
          "You want to build front-end business logic. This Tsugite is only for backstage and infrastructure APIs; business logic is handled by parts (Kernels)."
        ],
        "what_it_checks": [
          "It checks materials and versions. It does not send a request for a material not in the configured allowed_source_handles, a handle that is a URL or Windows path, a source_locality other than LOCAL_DESIGNATED, or a contract pin that differs from contract_schema_digest. An empty list allows nothing.",
          "It checks fixes, locations and terms of use. It does not send a request with no fix or two fixes, a digest of a different material, an empty revision, a range without an end, a start after the end, pages or lines numbered from 0, a whole-material location or anchor with a range, an unknown location kind, or no terms of use.",
          "It matches the fix. It does not accept a receipt that says RESOLVED although it does not match the fix, a receipt that silently returns a new revision, a REVISION_CHANGED that returns content or has no observation, a pinned_matches that contradicts the observation, a LOCATOR_UNRESOLVED whose pin does not match, or a receipt that does not return the pin.",
          "It treats materials as data. It does not accept a receipt claiming the material is executable, a receipt that allows embedded instructions (embedded_instructions_authorized is not false), or a receipt that presents the material as a checked fact.",
          "It matches sources. It does not accept KNOWN without source_identity, a guessed source state, or an UNKNOWN that gives a name.",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a receipt that does not return the terms of use or the location, a receipt claiming that an effect was carried out, extra fields, a receipt with no pin or a different version, or an INCOMPATIBLE_VERSION with the same pin as the request.",
          "It matches content. readContent reads only RESOLVED references, and it does not accept, and stops on, a receipt whose byte_size exceeds 1 MiB. Content exceeding the configured max_content_bytes is refused without being read (REFUSED). Read bytes are returned as bytes only when their length and sha256 equal the receipt's; otherwise it stops."
        ],
        "what_it_does_not_do": [
          "Being able to reference something is neither a checked fact nor a judgment that it may be used (every result carries approval: false).",
          "Because it does not interpret the content bytes, it does not judge the meaning of text in the material (whether it is instruction-like). Treating it as data is the receiving side's role.",
          "Comparing the fix with the original, extracting the location, and judging permissions, role scope and terms of use are the roles of the material Host and the Runtime. This Tsugite checks only the shape and combinations of the receipt, and cannot detect a Host that fakes an observation with the same value as the fix.",
          "It does not check whether the location really lies within the fixed revision or whether the extraction follows the unit (how pages are divided, how code points are counted, time positions). It compares only byte_size with the length and sha256 of the bytes read.",
          "It does not check whether a KNOWN source_identity was really recorded, or whether the material behind a handle really is a local material the user designated.",
          "Records exist only inside this Tsugite and are lost on restart. Reporting that an earlier reference has lapsed is the Runtime's role. It also does not check whether the answering port really has that version.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/material-reference/v1. On createMaterialReferenceClient({ cfg, port }), submit(request) asks for a reference, and readContent(request_id) receives the content of a RESOLVED reference as bytes. The result is one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). User provides: a port to the material Host (submit, optionally lookup, and readScoped for readContent) and cfg (version, list of materials and content size limit). Side effect: NONE.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-20",
        "category": "P/アプリ基盤",
        "kind": "Tsugite",
        "title": "Export and import round trip — Studio Round-trip and Migration Tsugite",
        "summary": "The Studio Round-trip and Migration Tsugite (継手, tsugite) carries requests (StudioRoundtripRequest) to the Runtime for exporting a candidate (EXPORT_RETURN), reading the export destination (INSPECT_EXPORT), verifying a Studio receipt (VERIFY_STUDIO_RECEIPT), a data migration preview (MIGRATION_PREVIEW), a migration decision (MIGRATION_DECIDE) and reading the migration status (MIGRATION_STATUS), and matches the receipt (StudioRoundtripReceipt). It neither overwrites nor retries automatically (fail-closed).",
        "problem": "In the round trip that returns work results to their original place, it prevents overwriting a file at the export destination, deciding a migration with an unverified Studio receipt or a diff the user has not checked, and resending an export or decision whose result is unknown so that something is written or decided twice.",
        "use_when": [
          "You want to export a candidate as a new file (create-new) and resolve an export with an unknown effect by reading the export destination.",
          "You want to send a data migration preview only when there is a Studio receipt for the same base package that has been verified and accepted (VERIFIED, ACCEPTED).",
          "You want to send a migration decision only when it has the same digest as the matched preview's diff and the user's own confirmation (recordConfirmation).",
          "You want to accept adoption (COMMITTED) only with a new generation (old generation + 1) and a new session, keeping the old generation.",
          "You want to resolve a decision with an unknown result by reading the migration status (MIGRATION_STATUS) and using the table of which decision took effect."
        ],
        "do_not_use_when": [
          "You want to delegate verifying a Studio receipt's signature, computing the diff, or the migration itself. That is the Runtime's role; this Tsugite only checks before sending and after receiving.",
          "You want to replace a file at the export destination. Exports create new files only; if anything is at the destination, the Runtime writes nothing and answers DESTINATION_EXISTS.",
          "You want to use a Studio receipt verification or a migration result as permission to run a package or as approval of adoption.",
          "You want a round trip that works right away. The counterpart entry points on the Runtime side (import, migration, reading the export destination and so on) exist only as a design or not at all, and the port is only a test stand-in. Real exports, reads, verifications and migrations: zero.",
          "You want to build business rules. This Tsugite is an application-platform Tsugite; business rules are handled by parts (Kernels) and by the composition."
        ],
        "what_it_checks": [
          "It checks the configuration and the shape of requests. It does not send a request when there is no configuration, the contract pin differs from contract_schema_digest, the export destination is not in destination_handle_refs (for reads too), a reference contains .., a drive, control characters or U+2028, the op is unknown, there are extra fields or fields of another op, the export applies itself automatically (automatic_apply), or the candidates are empty or repeated.",
          "It checks previews and decisions. It does not send a preview without a verified and accepted Studio receipt, for a different base package, or that failed re-verification; a decision for an unknown migration, with a different diff digest or without a confirmation; a confirmation that is not HUMAN, is bound to another decision, migration or diff, or has already been used; or a second decision on the same preview.",
          "It matches answers to Studio receipt verification. It does not accept an answer that lets an UNVERIFIED, MISMATCH or unaccepted Studio receipt proceed to preview, an ACCEPTED without the issued package's digest, or a VERIFIED without studio_receipt_id.",
          "It matches answers to decisions and migrations. It does not accept a receipt that answers ADOPT with DECLINED or DECLINE with COMMITTED, a receipt that does not return or swaps the decision, a COMMITTED that drops the old generation, keeps the same generation or skips a generation, or a receipt for a different migration.",
          "It matches answers about the export destination. It does not accept an EXPORT_PRESENT without found_bundle, with a different shape or that applies automatically, other states with found_bundle attached, an EXPORT_UNREADABLE without a digest or with a different target, DESTINATION_EXISTS for an op other than EXPORT_RETURN, or reads or refusals that claim an effect.",
          "It does not resend an export or decision whose result is unknown. An export is resolved by the record (lookup) or by reading the export destination, and a decision by the record or the migration status; until resolved, it sends nothing else. It exports again only on EXPORT_ABSENT, and writes nothing to a destination that holds a different bundle or an unreadable file.",
          "It matches receipts. It does not accept, and stops on (HALTED), a spoofed request_digest, a receipt for a different op, a state outside the op's table, a receipt claiming the original was edited, issued or re-signed, a receipt claiming a Studio receipt alone permits execution, a receipt with an escalated effect, a refusal reason that does not fit the op, or a receipt with no pin or a different version.",
          "If the migration status shows that a decision other than the one sent took effect, it does not treat this as an anomaly but returns it as a fact with other_decision_took_effect: true. It makes no further decisions on that migration."
        ],
        "what_it_does_not_do": [
          "Having verified a Studio receipt, being able to proceed to preview, or having migrated is neither permission to run a package nor approval of adoption (every result carries approval: false).",
          "It does not check a Studio receipt's signature, the trusted keys, the sha256 of the bundle or diff bytes, or the migration itself. It only matches the receipt's values.",
          "It does not check whether found_bundle really is content read from the file at the export destination, whether the read wrote nothing, or whether create-new really replaces nothing. Against a Runtime that breaks create-new, it can protect only destinations it has read and found to hold a different bundle or an unreadable file, and only until restart.",
          "After an answer that needs a person's decision (needs_human_decision: true), it does not know what the person did. Stopping and asking a person is the caller's job.",
          "Matched Studio receipts, previews, user confirmations and export destination records exist only inside this Tsugite and are lost on restart.",
          "It cannot tell whether a state such as COMMITTED is the effect of the decision this Tsugite sent or of the same decision sent by another sender.",
          "When an unknown export or decision result cannot be resolved (no record, the status stays CREATED or PREVIEWING, or the read is refused), it stays UNKNOWN and stops, waiting for a person's check.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority. It also does not change the contract (the schema authority is NOT_SPECIFIED)."
        ],
        "integration": "Contract: erabazu.candidate/certified-tsugite/studio-roundtrip/v1. On createStudioRoundtripClient({ cfg, port }), recordConfirmation receives the user's confirmation, and submit(request) returns one of DONE, ALREADY_DONE, REFUSED, UNKNOWN, RECEIPT_REJECTED or HALTED (all with approval: false). User provides: a port to the Runtime (submit, and optionally lookup) and cfg (version and destination_handle_refs). Side effect: LOCAL_REVERSIBLE for export, preview and adoption; NONE for reads, verification, declining and status.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-21",
        "category": "P/アプリ基盤",
        "kind": "Component",
        "title": "Typed answer intake — Typed answer intake part (Component)",
        "summary": "The Typed answer intake part (Component) is a part that checks an AI answer string against a fixed envelope (status, binding, and the content of each branch) and the decision schema passed by the caller, and sorts it into a draft adoption candidate (PROPOSAL_CANDIDATE), HOLD or OUT_OF_SCOPE. It asks the part (Kernel) P-01, through a port, to read the answer and check its types; this part checks the branch rules and the eight binding keys. Even a PROPOSE answer is not adopted (NOT_ADOPTED).",
        "problem": "Behind an AI \"proposal\", whether the answer is for stale input is not visible. This part does not make a candidate of an answer with the wrong types, an answer to stale input, or an answer from a port that cannot be read (fail-closed), and stops an answer whose eight binding keys differ from those expected with STALE_OR_UNBOUND. Even if the model declares \"valid\" or the like in the answer, it does not take that as a basis.",
        "use_when": [
          "You want to check an AI answer string (answer_text, up to 4,194,304 code units) against the envelope { status, binding, proposal | questions | reason } and the caller's decision schema (proposal_schema), and receive it as one of PROPOSAL_CANDIDATE, HOLD or OUT_OF_SCOPE (intake).",
          "You want to check whether the answer is for the conditions the caller expects (the eight keys product_id, case_id, role, input_revision, input_digest, selection_digest, profile_revision and request_id) by exact match of UTF-16 code units. If they differ, it is STALE_OR_UNBOUND, and the differing keys are returned in a fixed order in detail.mismatched.",
          "You want to check the content rules of each status (PROPOSE has only proposal, HOLD only questions, OUT_OF_SCOPE only reason) after P-01 has answered that it matches. If they are broken, it is ANSWER_NOT_TYPED (stage: branch).",
          "You want to receive a type mismatch with the stage at which it failed (detail.stage: json, envelope, proposal, branch) and the P-01 issues (location and code, up to 20).",
          "You want to attach to the result which decision schema was used, by the digest (schema_digest) of the schema combining the envelope and the decision schema.",
          "You want the same result from the same answer and the same P-01 answer, from processing that does not throw, does not modify the input, and has no clock, random numbers or state."
        ],
        "do_not_use_when": [
          "You want to use PROPOSAL_CANDIDATE as adoption, storage or application to the outside. PROPOSAL_CANDIDATE is \"a draft whose types and binding match\"; it is not a statement that the content is correct, and not permission to adopt or store. Recording adoption is the caller's role.",
          "You want to check whether the content of proposal is correct or whether the basis of the answer holds. This part looks only at types and binding. Checking the basis is the caller's role.",
          "You want to receive numbers beyond the safe integer range exactly as numbers. proposal is returned as the value read with JSON.parse, so such numbers can be rounded.",
          "You want to connect an asynchronous port that returns a Promise in place of P-01. Asynchronous ports are not supported; it stops with PORT_FAILED.",
          "You want to move answers in this envelope into the candidate form of an existing decision schema. That conversion is the role of a separate adapter outside this part, and existing schemas are not changed.",
          "You want it to also handle confirming the person's identity or keeping the answer. This part does not keep answers and does not confirm identity.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_OPTIONS when options is not a plain object or has unknown names or getters; with PORT_MISSING when contract_check or schema_digest is not a function; with INVALID_SCHEMA_ID when schema_id is not a string of 1 to 256 characters; and with INVALID_PROPOSAL_SCHEMA when proposal_schema is not a plain JSON object (a class instance, getters, holes, non-finite numbers, more than 64 levels of nesting).",
          "It stops with PROPOSAL_SCHEMA_NOT_TRUSTED when P-01 cannot read, or does not trust (keywords outside the subset and so on), the schema combining the envelope and the decision schema.",
          "It does not let the answer through and stops with PORT_FAILED when P-01 throws, when the form of its answer cannot be read, when state is not done, when it has not looked for duplicate keys, or when an answer it said matches cannot be read as JSON or as the envelope (a false answer).",
          "It stops with INVALID_ANSWER when answer_text is not a string (including a boxed string) or exceeds 4,194,304 code units, and with INVALID_EXPECTED_BINDING (detail.path) when expected_binding is not a plain object holding exactly the eight keys as strings of 1 to 256 characters.",
          "Natural-language text, broken JSON, lone surrogates, answers over 1 MiB in UTF-8, duplicate keys, keys outside the envelope, an unknown status, a proposal outside the decision schema, and missing or mixed branch content stop with ANSWER_NOT_TYPED.",
          "After the type check passes, if even one binding key differs from what is expected (including a difference in case), it stops with STALE_OR_UNBOUND. When inspecting the values itself throws an exception (a Proxy trap and so on), it is INSPECTION_FAILED. Getters are not called.",
          "Fuzzing of 1,000,000 runs that compares the results against a separately written reference judgement (including random failures of a stand-in for P-01) confirmed that mismatches and fail-open cases were 0. The wiring with the real P-01 was also checked."
        ],
        "what_it_does_not_do": [
          "Every result carries adoption: NOT_ADOPTED, approval: false, authority: NOT_DERIVED and claim: NOT_DERIVED. Even for a PROPOSE answer it does not adopt, store or apply anything to the outside; the result is not an approval (result_is_approval: false) and creates no authority (derives_authority: false).",
          "The judgement of match or mismatch stays with P-01; this part only reads P-01's answer. It does not raise the standing of claims or their basis, and returns the content of proposal as it is.",
          "That P-01 works correctly is left to P-01's own inspection. What was checked here goes as far as the wiring with the real P-01.",
          "binding does not include an attempt ID. The AI does not know the executor's attempts, so the mapping between requests and attempts is handled outside this part.",
          "The content of the decision schema, adoption, storage and identity confirmation are decided by the caller outside this part. It does not keep answers either (its only state is the settings, which are frozen).",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use the output parser of a particular AI."
        ],
        "integration": "Create an instance with createTypedAnswerIntake({ contract_check, schema_digest, schema_id, proposal_schema }) in src/intake.js and call instance.intake(answer_text, expected_binding). For the ports, pass P-01's checkContract and schemaDigest (synchronous). It returns { ok: true, errors: [], status, binding, proposal, questions, reason, schema_digest } or { ok: false, errors: [{ code, detail? }] }, and does not throw. There are no side effects, no state and no credentials, and its only dependencies are built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      },
      {
        "id": "P-22",
        "category": "P/アプリ基盤",
        "kind": "Kernel",
        "title": "Adoption revision ledger — Adoption revision ledger part (Kernel)",
        "summary": "The Adoption revision ledger part (Kernel) is a standalone part that applies, one operation at a time on a ledger passed to it, the transitions of revisioned items (candidate, confirmation by a person, adoption, then superseded or revoked) and the confirmation records (applyAdoption), and returns per-item facts from the current conditions and the impact input (reviewAdoption). A new revision (including a correction) always enters as a candidate, and adoption happens only when there is a matching confirmation by a person for that revision. Old revisions, revoked entries and history are not deleted. inference and advice are always null.",
        "problem": "Behind \"adopted\", who checked which revision is not visible. This part prevents an AI from confirming, adopting or revoking; corrections from being adopted automatically; a resent old revision from being adopted over a newer adoption; superseded adoptions or history from being deleted; and an inconsistent ledger from being used.",
        "use_when": [
          "You want to add a new revision to the ledger as a candidate (CANDIDATE), as a revision larger than any earlier revision of that item (PROPOSE). source is PERSON or AI; an AI proposal goes into the candidate fields, kept apart from the confirmation fields.",
          "You want to record a confirmation by a person (actor_kind: PERSON) only when the content digest and every binding field are the same as the candidate's (CONFIRM). Even with the same digest, a difference in a binding field makes it a different thing.",
          "You want to record as adopted only a candidate that has a confirmation by a person, and make the previous adoption superseded (SUPERSEDED) (ADOPT). You want to make a candidate or an adoption revoked (REVOKED) (REVOKE, with reason_ref).",
          "You want to apply an operation together with the ledger revision (expected_ledger_revision) and, if the ledger has already moved on, apply nothing and receive LEDGER_CONFLICT.",
          "You want to compare each binding field of the adopted revision with the current conditions (context), and receive per item the differing fields (changed), the fields with no current value (unknown) and the reconfirmation state (CURRENT, RECONFIRM_REQUIRED, RECONFIRM_UNKNOWN, NOT_ADOPTED) (reviewAdoption).",
          "You want to pass impacts computed separately by the caller (AFFECTED, NOT_AFFECTED, UNKNOWN) as impacts, and receive them mapped to a per-item impact (NOT_AFFECTED, REVOCATION_CANDIDATE, IMPACT_UNKNOWN, IMPACT_NOT_PROVIDED, NOT_ADOPTED).",
          "You need the same facts from the same input without the clock, random numbers or the environment's locale (the time is passed by the caller as at), without throwing exceptions and without modifying the input."
        ],
        "do_not_use_when": [
          "You want to store the ledger. The new ledger in the result is a candidate before storage (committed: false); storage and atomic comparison are the caller's role.",
          "You want to check that actor_ref and actor_kind are really that person. It does not confirm identity (authentication) and records them as the caller's declaration.",
          "You want it to compute impacts, or to check that the impact input is correct. The impact input is only copied, and an impact that was not passed is not made \"no impact\" but IMPACT_NOT_PROVIDED.",
          "You want it to carry out revocation. REVOCATION_CANDIDATE is a copy of the impact input, not the execution of a revocation. A REVOKE operation is applied only when it is passed as an operation by a person.",
          "You want the part to decide the meaning of binding fields (which field is the revision of a material). The names of the fields are decided by the caller.",
          "You need ledgers beyond the limits (10,000 entries, confirmations and impacts, 100,000 events) or speed on large ledgers. Matching confirmations is quadratic in the count, and speed on large ledgers has not been checked.",
          "You use it on a version other than Node.js v24.21.0. Other versions have not been checked (NOT_VERIFIED)."
        ],
        "what_it_checks": [
          "It stops with INVALID_INPUT when the argument is not a plain object, has unknown names or getters, or lacks a field; with INVALID_LEDGER when the ledger is malformed (schema, revision numbers, digests, binding, source, state, a confirmation whose actor_kind is not PERSON, the op of an event and so on); and with TOO_MANY_ENTRIES when a count limit is exceeded.",
          "When the internal consistency of the ledger does not hold (ledger_revision against the number of events, gaps in seq, the same item and revision pair twice, two adoptions, confirmations that do not match an existing candidate, an adoption or superseded adoption without confirmation), it stops with LEDGER_INCONSISTENT and does not use that ledger.",
          "When an operation is malformed (an unknown op, names or getters not belonging to that op, a missing field, an empty item_id, a revision that is 0, a fraction, a string or an unsafe integer, an uppercase digest, binding fields with differing names, values or count, an unknown source, a missing actor_ref or at), it stops with INVALID_OPERATION.",
          "It stops with CONFIRMER_NOT_PERSON when the actor_kind of CONFIRM, ADOPT or REVOKE is not PERSON; with CONFIRMATION_MISMATCH (path is the first difference) when the digest or a binding field of CONFIRM differs from the candidate; and with NOT_CONFIRMED when the revision for ADOPT has no confirmation by a person.",
          "It stops with REVISION_NOT_NEWER when the PROPOSE revision is not larger than earlier revisions; with UNKNOWN_ENTRY when the item and revision pair does not exist; with NOT_A_CANDIDATE when the target of CONFIRM or ADOPT is not a candidate; with STALE_REVISION when there is a newer adoption; and with NOT_REVOCABLE when the target of REVOKE is superseded or already revoked.",
          "It stops with INVALID_CONTEXT when context is malformed; with INVALID_IMPACTS when impacts is malformed or has the same revision twice; and with INSPECTION_FAILED when inspecting the values itself throws an exception (a Proxy trap and so on). When it stops, it returns the code and location (path, a JSON Pointer) of the first problem.",
          "Fuzzing of 1,000,000 runs that compares the results, one operation at a time, against a state model written only with linear search confirmed that mismatches and fail-open cases were 0."
        ],
        "what_it_does_not_do": [
          "It returns only the ledger transitions and per-item facts. inference and advice are always null, and it makes no judgements. ADOPTED is the fact that \"a revision confirmed by a person was recorded as adopted\"; it is not a statement that the content is correct, nor an approval (result_is_approval: false). It creates no authority either (derives_authority: false).",
          "It does not adopt corrections (new revisions) automatically. No operation deletes items, confirmations or events.",
          "After a newer adoption is revoked, a person can adopt an older confirmed candidate again. STALE_REVISION occurs only when there is a newer ADOPTED.",
          "Storing the ledger, atomic comparison, confirming identity, carrying out revocation and computing impacts are done by the caller outside this part. The result of the impact computation is received as impacts, and its meaning is not reworked.",
          "It has no external boundary, no state, no clock and no random numbers. Side effect: NONE, and it uses no credentials.",
          "Passing the workshop inspection is not certification. Shipment eligibility has not been evaluated, it has not been published, and it grants no authority.",
          "It is not a template for how to use a particular workflow engine."
        ],
        "integration": "Call applyAdoption({ ledger, operation }) and reviewAdoption({ ledger, context, impacts }) in src/adoption-ledger.js (neither throws). An empty ledger is schema \"erabazu.kernel/adoption-ledger/v1\", ledger_revision 0 and three empty arrays. It returns { ok: true, errors: [], facts } (the new ledger with committed: false and transition, or items; inference and advice are null) or { ok: false, errors: [{ code, path }] }. There is no port, and its only dependencies are the Node.js built-ins.",
        "license": {
          "identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
          "text_url": "licenses/LICENSE_JA.md",
          "english_text_available": false,
          "other_licenses": []
        },
        "test_summary": {
          "status": "Workshop technical inspection PASS",
          "fuzz_runs": 1000000,
          "mismatches": 0,
          "fail_open": 0,
          "node_version": "v24.21.0",
          "note": "Results from preserved inspection records; live service connection and shipment eligibility are separate."
        }
      }
    ]
  },
  "inspection_count": 164,
  "total_catalog_count": 165,
  "complete_claimed": false,
  "license_identifier": "LicenseRef-WIT-ERABAZU-Parts-1.0",
  "english_license_text_available": false
}
