コンテンツにスキップ

マニフェスト

本書は AGENTS.md の分冊(正典・AGENTS.md と同格)。優先順位は「実コード → AGENTS.md(本書を含む)→ DESIGN.md → PRODUCT.md」。 節は AGENTS.md から逐語で移設した(PR #nnn・2026-09-16)。対象パス: apps/web/src/pages/manifest/**` `packages/shared/src/schemas/manifest.ts` `packages/shared/src/constants/waste.ts` `supabase/migrations/*manifest*。この領域に触るワーカー / レビュアは本書を読む。

KYO-56 マニフェスト交付区分(不変条件ラチェット・mig128)

マニフェストの交付区分・JWNET 状態・報告年度を将来の交付等状況報告で消費するための器。D-K1/D-K2 の自治体判断ロジックは本スライスに含めない。

  • 状態モデル: manifests.delivery_methodpaper / electronic(既存行互換の NULL 可)、manifests.jwnet_statenot_registered / registered / cancelled(既定値 not_registered)。Form 3 の集計除外は共有純関数の electronic && registered のみで導出し、paper・未登録・取消は集計対象とする。
  • JWNET 報告対象(wl-wz9・mig181): delivery_method='electronic' のマニフェストだけを対象とし、paper と NULL(未設定)は fail-closed で除外する。jwnet_state='registered' は条件に入れてはならない(登録報告の自動更新経路がまだ無く、加えると送信候補が構造的に常に空になって到達不能になる)。登録報告を自動更新するスライスで初めて状態条件を追加する。UI の候補判定と DB の jwnet_submission_authz_ok の両方で弾き、共有判定は isJwnetReportable に一元化する。Form 3 の isExcludedFromForm3(電子かつ登録済み)とは式を混ぜない。
  • 採番時も再検証(mig182): enqueue 後にマニフェストを paper へ戻す迂回を塞ぐため、assign_web_edi_request_code は submission と manifest を同時ロックし、delivery_method='electronic'jwnet_submission_authz_ok を再検証する。UPDATE の WHERE にも電子判定を再掲し、ROW_COUNT 再検査と合わせて TOCTOU を閉じる。
  • 越境列 denylist の再評価(mig182): 越境 UPDATE policy を持つ manifests へ列を足すときは mig89 の denylist を再評価する。mig128 が交付区分・JWNET 状態・報告年度を足したとき更新漏れがあり、担当 collector が他社の法定分類を書き換えられたため、mig182 で delivery_method / jwnet_state / reporting_fiscal_year を denylist に追加した。carrier_person_name は運搬側所有情報のため denylist に足さないと mig221 で判断した。
  • 報告年度: reporting_fiscal_yearissue_date から 4 月始まりで導出する(4 月以降は暦年、1〜3 月は前年)。導出 trigger は BEFORE INSERT OR UPDATE の全列発火とし、UPDATE OF に限定してはならない。導出関数は search_path = '' を固定する。
  • backfill: 既存行は delivery_method='paper'jwnet_state='not_registered'issue_date 由来の年度を NULL の列にだけ埋める。非 NULL の既存値を上書きせず、再実行しても非破壊であること。
  • 列 guard: 本スライスでは staff の手動設定を許容するため client 列 guard は追加しない。JWNET 自動更新を導入するスライスで INVOKER guard を追加する。
  • 恒久チェック: scripts/hardening-sweep-ratchet.mjs / hardening-sweep-ratchet.test.mjs は mig127 以降の年度導出 trigger が UPDATE OF に戻っていないことも検査する。Docker proof は scripts/proofs/mig128-delivery-classification.sql を含む scripts/proofs/*.sql 全流しで [NG]=0 を確認する。

マニフェスト混合廃棄物(不変条件ラチェット・mig192)

廃棄物種別の複数チェックと混合報告名の導出。実体: packages/shared/src/constants/waste.tspackages/shared/src/schemas/manifest.tsapps/web/src/lib/manifest-waste-type.tsManifestWasteTypeSelector.tsxsupabase/migrations/00000000000192_mixed_waste_type.sql

  • WASTE_TYPES は法令分類 + 実務上の混合 3 区分(管理型/安定型/建設混合廃棄物)。混合 3 種は法令上の単独分類ではなく JWNET の廃棄物の種類コードに対応しないマニフェスト以外のピッカー(許可証・車両・単価・案件・CSV 取込)には混合 3 種を出さない(法令分類のみ)。
  • 導出は deriveMixedWasteType が単一権威: 重複除去後 2 件以上で、全部が安定型 5 品目(廃プラ・ゴムくず・金属くず・ガラスくず等・がれき類)なら安定型、1 つでも外れれば管理型(その他 は外れる側)。0/1 件では自動導出しないが、大分類を明示した 0/1 件の内訳は保存を許可する。建設混合廃棄物はこの関数から返さない(大分類セクションで事務が明示選択する)。
  • 2段 UI: 内訳セクションは LEGAL_WASTE_TYPES(法令分類)のみ、大分類セクションは混合 3 種(管理型/安定型/建設混合廃棄物)に分け、構造的に併選できない。内訳 0 件 + 大分類単独、内訳 1 件 + 大分類指定も許可する。混合廃棄物が内訳 0/1 件でも入ると Form3 では waste_type 単位でグループ化されるため、法的な扱いは後続レビュー事項とする。
  • 保存と上書き: 報告名の決定は shared の resolveManifestWasteType、復元の判定は shared の restoreManifestWasteTypeComponents を単一権威とする。内訳 2 件以上では deriveMixedWasteType の自動大分類をチェックし、事務は別の大分類へ手動上書きできる。上書き中は「自動判定は…」を画面に明示し、上書き状態は DB 列ではなく保存済み 2 列からの再計算と画面状態で導く。単一選択は waste_type_components = null、内訳 1 件 + 大分類は内訳 1 件を保存する。
  • 作成フォームの既定は未選択([]。既定チェックはチェックボックス UI では加算的に残り、内訳汚染と混合 3 種の disable を生む(QA 2026-08-04 所見 1)。
  • enum 外の既存 waste_type(OCR 由来の生文字列)を編集で強制置換しない。内訳未選択のまま他項目を更新するときは waste_type / components を PATCH に含めない(据え置き)。
  • waste_type_components は mig182 の越境 denylist に追加済み(担当 collector は書換不可)。manifests へ列を足すときの denylist 再評価の実施例は mig192(本列)→ mig211(事業場名 2 列)→ mig216(返送 3 列)→ mig218(正味重量)と続く。
  • mig211 の排出元・処分事業場名: manifests.source_site_name / disposal_site_name(nullable text)は、二次搬出レビューで配車 stop の回収先名をデフォルトにし、事務が上書きできる法定スナップショット。両列は mig182 の越境 denylist に追加済みで、担当 collector は越境で書き換えられない。proof は scripts/proofs/mig211-manifest-site-names.sqlM211-CROSS)。
  • 回帰: scripts/proofs/mig192-mixed-waste-type.sql(owner allow / collector deny の正確メッセージ照合 / 値不変)を全流しで検証。

紙マニフェスト返送期限管理(不変条件ラチェット・mig216)

返送受領の記録だけを管理する。送付側の10日義務は対象外、特管なし、期限は B2/C2/D/E の 90/90/90/180 日固定(B2/D/E はユーザー確定 2026-08-12、C2 は mig219 で追加)。対象は delivery_method !== 'electronic'(NULL は紙扱い)で、isJwnetReportable / isExcludedFromForm3 と式を混ぜない。

  • B1 は受領記録のみで返送期限を持たない(mig219)RETURN_DEADLINE_DAYS に B1 を足しては ならず、manifestReturnStatus の overall も期限管理対象(B2/C2/D/E)だけから導く。
  • manifests.b1_received_on / c2_returned_on は mig219 の追加列(B1=運搬受託者控えのちぎり 保持日、C2=処分終了後に運搬受託者へ返送された受領日)。mig221 の越境 collector denylist に 両列とも入っており、担当 collector は書き換えられない。
  • manifests.b2_returned_on / d_returned_on / e_returned_on は nullable date の受領日列。 staff の既存所有テナント UPDATE 経路を使い、新規 policy / 列 guard / issue_date との大小 CHECK / backfill は追加しない。未来日等は client 検証とする。
  • manifests_guard_stage_update() は直前の最新定義(mig211)を全条項写経し、越境 collector denylist に新3列を is distinct from で追加する。担当 collector は返送受領記録を変更できない。
  • shared の manifestReturnStatus が期限状態を一元導出し、受領済みは期限を問わず returned、未受領は pending / approaching(残り30日以内)/ overdue(期限翌日から)とする。proof は scripts/proofs/mig216-manifest-return-tracking.sql。全 proof を流し [NG]=0 かつ psql exit 0 を確認する。

マニフェスト正味重量(不変条件ラチェット・mig218)

manifests.net_weight_kg は数量・単位と別の nullable numeric(12,3)(未 backfill、0 以上・NaN 拒否。±Infinity は精度付き numeric の 22003 が型で弾く)。 - 作成・OCR・レビュー・collector 編集/表示・一覧の各経路で扱う表示・記録用の列で、JWNET 電文には載せない(D09/D60 の報告数量は既存の数量+単位経由のまま。式を混ぜない)。数量/単位と分類列は変更しない。 - OCR は同一呼び出しの出力拡張のみ(追加課金なし)。netWeightKg は必須 4 項目に含めず、per-field 低信頼判定の対象にだけ加える。 - manifests_guard_stage_update() は mig216 の全条項を維持し、越境 collector denylist にだけ net_weight_kg を追加する(実施系譜 192→211→216→218 の末尾)。 - 編集フォームは空欄を据え置き(PATCH に netWeightKg を含めない)。値のクリア UI は本スライスに無い(必要になったら明示的なクリア操作として設計する)。 - 回帰は scripts/proofs/mig218-manifest-net-weight.sql(owner allow / collector deny のメッセージ照合 + 拒否後の値不変 / named CHECK の constraint_name 照合 / sentinel mig218_manifest_net_weight_applied())を全流しする。

二次搬出マニフェスト写真(不変条件ラチェット・mig175・DB 層)

二次搬出の便ごとにドライバーがマニフェスト写真を撮って完了し、事務が画像 + OCR 内容を レビューキューでチェックする機能の器。設計の正典は docs/superpowers/specs/2026-07-30-secondary-manifest-photo-design.md(wl-8lu・ユーザー確定 3 分岐 = 原則必須・理由付きスキップ可 / 撮影直後 OCR / 既存レビュー統合)。実体: supabase/migrations/00000000000175_secondary_manifest_photo.sqlscripts/proofs/mig175-secondary-manifest-photo.sql。UI / API は後続 PR(本節を拡張する)。

  • 便リンクは ocr_import_items.dispatch_stop_id が唯一の器: client は同一テナントの実在 stop にのみ設定でき(guard が current_tenant_id() 比較で fail-closed)、設定後は immutable(付け替え・剥がしは raise。便の証跡が別の便に化けるため)。staff の 「null → 後付けリンク」は同一テナント検証つきで許可(バルク取込の訂正)。
  • driver の書込は列 whitelistocr_import_items_guard_driver_write・mig100 の写経): INSERT は自分の担当かつ二次搬出の stop 必須・manifest_id/reviewed_by/reviewed_at null 必須・status は pending/processing のみ。UPDATE で書けるのは image_path/status/ raw_json/confidence/low_confidence_fields/error のみで、status を confirmed/ discarded へは遷移不可(レビューの自己確定と破棄の迂回を封鎖)。
  • driver RLS は own-batch スコープ: batches は created_by = 自 profile(権威は mig123 の fill トリガ・偽装不可)、items は親 batch の所有で判定。同一テナントでも他人の batch/item は 不可視。mig123 proof の IM2 はこの新契約に更新済み(旧「driver 全面不可視」の期待に 戻さない。batch 0009 は fill の created_by 由来 tenant 導出で「driver 本人所有」になる fixture である点に注意)。
  • dispatch_stop_manifest_evidence は便ごとの証跡の単一権威(1 便 1 行 unique): kind in ('photo','skipped','manual')manual は mig215 で追加・下の mig215 節参照)・ skipped は理由必須(btrim <> '' CHECK)・photo / manual は理由 null 必須・photo → 非 photo の後退は raise(写真証跡を理由文で上書きさせない・skipped → photo の回復は可)。 tenant_id は stop から再導出・created_by/created_atfill_audit_created_by(mig171) でサーバー所有。二次搬出以外の stop への INSERT は raise(⚠一覧の母集合を汚さない)。 書込は staff または担当ドライバー本人のみ・DELETE policy なし。
  • 「photo ⇒ item 必須」は CHECK にしない: FK が on delete set null のため、CHECK だと SET NULL が違反になり item の物理削除(運用経路)を隠れて塞ぐ。client の INSERT / photo への 遷移 / item 差し替え時に fill が検証し、裏付け item は同一テナントかつ同一 stop に リンク済みであることまで要求する(他便・他人の item の uuid を裏付けに刺せない)。
  • 完了ゲートは DB で強制しない(mig141/152/172 の完了統制と絡むため)。担保は事務の ⚠一覧が「完了済み二次搬出 stop 起点の左結合」で導出すること(UI は後続 PR)。evidence 起点にすると admin のボード完了・PostgREST 直叩き・導入前完了が黙って漏れる。
  • codex terra 敵対レビュー済み(P1×4 を修正・2026-07-30)。以下は再導入禁止:
  • photo の裏付け item は image_path 非 null 必須(無いと PostgREST 直叩きで 「写真を出した」証跡を偽造できる)
  • 提出後の item は driver から凍結old.status が pending/processing のときだけ driver UPDATE 可。needs_review/ready を書き換えられるとレビュー候補の撤回・raw_json 改ざんになる。撮り直しは新 item を作る)
  • UPDATE でも現在の担当 stop を再照合(batch 所有だけだと再割当後の旧 driver が 新担当者のレビュー候補を書き換えられる)
  • 確定済み(confirmed)item を指す証跡の差し替えは driver 禁止(staff の再レビュー 経路は別スライス)
  • batch の driver UPDATE は active → done|rate_limited の status 遷移のみ・items の driver policy は親 batch の deleted_at is null を要求
  • skipped の理由は書き換え不可(訂正履歴が無いため。訂正は skipped → photo か 履歴付き訂正 RPC = 別スライス)
  • evidence の監査列は専用 filldispatch_stop_manifest_evidence_fill_audit)。 mig171 の共通 fill を張ってはならない — UPDATE で無条件に OLD を復元するため、FK の 実 ON DELETE SET NULL(退職処理 = auth.users 削除)まで巻き戻して 23503 で止める。 carve-out は mig123 batches fill と同型(pg_trigger_depth() > 1 + 旧 profile 消滅)
  • mig123 の fill 2 関数へ alter function ... set search_path = '' を後付け(本文は 完全修飾済みのため属性のみ・再写経しない)
  • WHERE 付き UPDATE は SELECT policy の可視性も要求する(PostgreSQL 仕様・MUT-D/F で 実証): items の「他人の行を 0 行にする」実効層は SELECT の own-batch であり、 UPDATE-USING 単独の緩みは実害に至らない。SELECT を緩める変更は MP20 が捕捉する
  • UI 契約(PR B・web): driver の完了フローは証跡(storage → batch+item → evidence)を 完了より先に永続化し、OCR は完了を待たせず発火(失敗は item=failed で残す)。順序は manifest-evidence.test.ts のミューテーション(upload 前に evidence を作る改悪で 2 件落ちる) で固定。⚠要確認一覧は完了済み二次搬出 stop 起点の左結合buildMissingPhotoRows・ evidence 起点にすると admin のボード完了・直叩き・導入前完了が黙って漏れる)。母集合は 直近 14 日かつ MANIFEST_EVIDENCE_SINCE='2026-08-12' 以降に clamp し、導入前の完了や 古い stop を要確認へ戻さない。 useReviewQueuefailed をキューに含める(従来は failed が画面に出ず滞留した。 再解析 = reanalyzeOcrItem は throw せず結果を返し、原本画像の無い item は patch しない)。 便バッジ(stopBadgeLabel)は取引先名を出さないstop-labels.ts の越境マスキングと 同じ扱い)。/manifests/ocr の認可は canRequestManifestOcr(= canCreateManifest + collector driver)で、manifests の作成・確定の境界(canCreateManifest)は driver に 開いていないことをテストで固定(manifests.test.ts)。
  • 回帰: scripts/proofs/mig175-secondary-manifest-photo.sql(MP-SENT/MP1–31・allow/deny 両側・ミューテーション 5 系統 = driver whitelist / evidence fill / stop link / audit fill / SELECT 緩め、で proof の歯を確認済み)+ 更新した mig123 proof を scripts/proofs/*.sql 全流しで検証([[local-docker-proof-must-run-all-proofs]])。

二次搬出コボレーン確認(不変条件ラチェット・mig201)

dispatch_stop_manifest_evidencekobolane_status / kobolane_confirmed_at / kobolane_skip_reason の3列を加え、マニフェスト証跡と同じ1行へ確認の事実を記録する。 confirmed は SECURITY INVOKER の client guard が pg_catalog.clock_timestamp() を強制する(now() は Tx 内で不変のため K1b のドリフト検査が値差で判定できない=now() へ戻してはならない)。 confirmed → skipped の後退と skipped 理由の書換を拒否する。skipped は理由必須で、 既存行互換の status NULL は3列すべて NULL とする。~~コボレーン写真は確認の儀式として 撮影するだけで、アップロード・読取・保存を意図的に行わない~~ — この方針は mig233 で撤回した (ドライバーがマニフェスト写真と取り違える事故が頻発したため保存して事務が突合する)。 現行の契約は「コボレーン写真の保存と一時保管(mig233 / mig234)」の節が正典。 完了ゲートはマニフェスト撮影とコボレーン確認を順不同で並列に扱い、両方揃った後に内容確認の明示ボタンで完了する (#522・方式D・migration なし)。判断は純関数 resolveSecondaryCompletioncompletion-gate.ts{ manifestDone, kobolaneDone, canComplete } を導出)と hook useStopCompletionGate.maybeCompleteCollectorDriverHome.tsx)に集約し、maybeCompletecanComplete(=manifestDone && kobolaneDone)が 真のときだけ upsertKobolaneEvidencedoComplete を実行する。DB 書込順は必ずマニフェスト永続 → コボレーン UPDATE → doComplete に固定する(順不同なのは UX 操作順であって書込順ではない): コボレーン確認は upsertKobolaneEvidencedispatch_stop_manifest_evidence既存行を UPDATEして 記録し、その行はマニフェスト証跡(insertPhotoEvidence/insertSkipEvidencekind NOT NULL)が 先に作るコボレーン先行の UX でも upsertKobolaneEvidence を先に呼んではならない: 更新対象の行が 無く「マニフェスト証跡がありません」で二次搬出が一切完了できなくなる(#511 で kobolane→manifest に 反転させ本番 P0=完了不能を出し #520 revert・#522 で順不同化)。コボレーン先行はローカル state (kobolaneLocal)に保持し、マニフェスト証跡ができてから UPDATE する(独立コボレーン証跡の migration は 不要)。不変条件は CollectorDriverHome.completion-gate.test.tsx が担保(コボレーン先行を駆動して 証跡行ができるまで upsertKobolaneEvidence/doComplete を呼ばないこと + invocationCallOrder で persist < upsert < complete を固定)+ manifest-evidence.test.ts の「行なしで throw」。OCR は撮影時に 非同期発火する。DB で完了自体を強制しない方針は mig175 と同じ。事務のボード完了は対象外。実体: supabase/migrations/00000000000201_kobolane_confirmation.sqlscripts/proofs/mig201-kobolane-confirmation.sqlapps/web/src/lib/dispatch/completion-gate.ts

二次搬出 手動マニフェスト証跡(不変条件ラチェット・mig215/mig248)

写真なしで完了した二次搬出便を、事務の手入力マニフェストで解消する経路(mig175 の証跡契約への 加法・driver 撮影フローは非破壊)。実体: supabase/migrations/00000000000215_manual_manifest_evidence.sqlsupabase/migrations/00000000000248_manual_evidence_after_discard.sqlapps/web/src/lib/db/manifest-evidence.tsinsertManualEvidence)・ManifestCreateForm (⚠要確認一覧 → /collector/manifests/new?dispatchStopId=... の prefill 遷移)。

  • kind='manual'manifest_id と相関: CHECK で manual ⇔ manifest_id not null (photo / skipped は null 必須)。skip_reason は skipped のみ必須・photo / manual は null 必須。 FK は on delete set null だが、manifests は法定表で物理削除が遮断されているため実務上到達しない。
  • manual は staff 限定: driver ロールは INSERT / UPDATE とも raise(42501・横乗り driver も 同様)。manual の manifest_id は INVOKER helper が呼出元テナントの実在 manifest であることを 検証する(越境 manifest ID の注入拒否)。
  • 後退規則: photo → 非 photo は原則 raise(mig175 踏襲)。ただし mig248 で、photo → manual は裏付け item が NULL、status='discarded'、または image_path IS NULL のときだけ救済する (写真証跡として機能しない状態に限る)。利用可能な item を指す photo → manualphoto → skipped は引き続き raise し、メッセージはそれぞれ photo evidence cannot be downgraded from photo to manual / photo evidence cannot be downgraded from photo to skipped と遷移先を明示する。manual → skipped は raise(手入力証跡を理由文で上書きさせない)。skipped → manualmanual → photo は解消・回復方向として許可する。
  • ⚠要確認一覧: buildMissingPhotoRows は利用可能な photo と manual を解消として除外し、 item が無い / 破棄済み / 画像パス無しの photo だけを manual 救済用に残す (完了済み二次搬出 stop 起点の左結合契約は mig175 のまま不変)。
  • 回帰: scripts/proofs/mig215-manual-manifest-evidence.sql(sentinel mig215_manual_manifest_evidence_applied())と scripts/proofs/mig248-manual-evidence-after-discard.sql (sentinel mig248_manual_evidence_after_discard_applied())を scripts/proofs/*.sql 全流しで検証。

コボレーン写真の保存と一時保管(不変条件ラチェット・mig233 / mig234)

~~コボレーン確認は mig201 で「写真は撮るが保存しない確認の儀式」だったが、ドライバーがマニフェスト写真と 取り違える事故が頻発したため、mig233 で保存し、事務が両方を見比べて検出できるようにした。mig234 で 「事務が確認したら削除する」一時保管にした。~~ この事務突合・確認運用は 2026-08-20 のユーザー裁定で撤回した。 取り違え防止は driver 自身の完了前確認と 撮り直しへ寄せ、事務側ではコボレーン写真を表示しない。マニフェスト確定が成功した後にだけ、既存の confirmKobolanePhoto(Storage 削除 → 削除記録)を staff 操作として呼ぶ。削除失敗はマニフェスト確定を 巻き戻さず警告にする。

  • コボレーン写真は法定証跡ではない(マニフェスト写真とは扱いが違う)。バケットは kobolane-photos(private・新設)で、manifest-import-images(法定マニフェスト原本)を流用しない。 driver は自テナント第 1 フォルダへの INSERT のみ(SELECT / UPDATE / DELETE なし)、 staff は自テナントの SELECT と DELETE のみ。service-role 経路は作らない。 manifest-import-images に原本を消せる DELETE/ALL policy を足してはならない。
  • QA-6 の差分は mig262 で是正済み: manifest-import-images creator delete は破棄済み item の画像だけ staff(従来のdischarger作成者も維持)が除却可。tenant_id=current_tenant_id()image_path=storage.objects.namestatus='discarded'manifest_id is nulldeleted_at is null を全て要求する。無条件撤去しないのは、レビューキューが status の破棄保存後に画像を best-effort で除却する正規フローを維持するため。確定済み・レビュー中の画像は保持し、item の無い孤児objectも除却不可(受容)。policyの名前/コマンド集合・DELETE 0行とobject不変・discarded除却1行・kobolane staff除却の非退行は mig262 proof で固定する。
  • コボレーンに OCR を走らせない(課金しない・レビューキューを荷台の写真で汚さない)。
  • kobolane_image_path は NULL を常に許す。 機能導入前の confirmed 行は写真を持たないため、 NOT NULL や「confirmed なら必須」の CHECK を入れると既存行が壊れる。
  • 削除は「パスを消す」のではなく「削除済みを記録する」。 guard の kobolane image path cannot be cleared を緩めず、kobolane_image_deleted_at を足して Storage から実体を消したことを記録する。写真ごと記録を消すと「本当に撮ったのか」が後から分からなくなる。 非 NULL の削除時刻を NULL へ戻せない。パスが NULL のとき削除時刻を入れられない。
  • 順序は「Storage の削除 → deleted_at の記録」。逆にしてはならない。 逆順だと記録が成功して 削除が失敗したときに「削除済みと書いてあるのに実体が残る」。この順なら記録に失敗しても 「実体は無いが削除時刻が無い」で次回の操作で復旧できる。 ~~- 画面は 4 状態を区別する: 写真あり / 写真は削除済み / 写真なし(機能導入前) / 取得失敗。 取得失敗を「写真なし」に化かさない(fail-closed 表示規約)。~~ これは撤回済みの事務表示契約である。 現行の事務 UI はコボレーン写真を表示せず、DB の列・guard・ Storage policy は引き続き fail-closed の不変条件として保持する。
  • guard(SECURITY INVOKER)は mig201 / mig204 の全条項を一字も落とさず全文逐語で create or replace する。skipped からの後退禁止は mig204 の条項であり、削ってはならない (述語が is distinct from 'skipped' なので skipped → confirmed も塞がる。これは mig204 の proof が 検査していない副作用だが出荷済みの挙動で、回復経路を開けるかは wl-ct6 で別途判断する)。 ~~- 未確認のまま残る写真の掃き出しは未実装(定期実行の基盤が無い)。事務が確認しない便の写真は残り続ける。~~
  • マニフェスト確定成功後にコボレーン写真を削除する。 確定が先で、既存 helper の Storage 削除→ kobolane_image_deleted_at 記録の順序を使う。削除失敗時も確定済み状態を維持し、警告だけを出す。
  • 回帰は scripts/proofs/mig233-kobolane-photo.sql / mig234-kobolane-retention.sql / mig238-kobolane-integrity.sql。削除→記録の順序は web テストで固定し、 逆順にすると赤になることを確認する。

mig238 で塞いだ穴(QA 2026-08-19・再導入禁止

  • kobolane_image_deleted_at は staff(admin|normal)だけが変更でき、値は clock_timestamp() に統制する。 mig234 の guard は 非NULL → NULL しか塞いでおらず NULL → 非NULL は誰でも書けた。driver がこれを立てると、写真は Storage に実在するのに ~~事務画面は「削除済み」を表示し二度と突合されない = mig233 の存在理由が消える。~~ この画面表示・突合の記述は撤回済みで、事務側にはコボレーン写真を表示しない。 proof P234-1 は client 供給時刻がそのまま保存されることを正として固定していたので反転済み。 検査があること自体は安全を意味しない — 何を正しいと決めているかを疑う。
  • コボレーン写真のアップロードは完了時に行う(撮影時ではない)。撮影時に上げると、 完了せず離脱した分が紐付かない孤児画像として無期限に残る(driver に SELECT / DELETE が 無いので後始末もできない)。#604 が撮影と完了を分離したことで窓が無限化した。
  • kobolane-photos の INSERT は driver と collector staff の両方に許す。 driver 限定にすると can_drive の事務が 42501 で保存できずスキップ理由でしか完了できない (配車ボードは canActAsDriver で staff を割当可能)。manifest-import-images と揃える。
  • 二次搬出の完了は常に確認ダイアログを通す。 証跡が揃っていると即完了する分岐があると、 再完了・再開で明示確認を通らない(#604 の目的が満たされない)。 ~~- 事務画面は skipped理由つきで表示する。写真なし(機能導入前) に潰さない。~~ この事務表示契約は 2026-08-20 に撤回した。 skipped の理由と後退禁止は DB guard の不変条件として 残すが、事務画面でコボレーン証跡を表示する配線は持たない。

二次搬出の完了は明示ボタン(不変条件ラチェット・2026-08-18)

証跡(マニフェスト + コボレーン)が揃っても自動完了してはならない。ドライバーが記録内容を 確認してから「この内容で完了」を押す。

  • 証跡確認の各ハンドラから完了呼び出しを外す。完了ボタンは canComplete のときだけ押せ、 揃うまでは何が残っているかを出す。
  • 証跡は完了より先に永続化する(storage → batch+item → evidence → 完了)。
  • 担当本人と横乗り(mig177)は useStopCompletionGate同じ統制を通す。複製しない。
  • driver は Storage を SELECT できないので、確認用のサムネイルはセッション中の blob からのみ。 blob が無ければ画像を出さず文言だけにする(壊れた <img> を出さない)。
  • 通常回収(sourceSiteId == null)はこのゲートを通らない。挙動を変えない。