店舗作成・初期設定
店舗作成・初期設定
Section titled “店舗作成・初期設定”Document ID: SRS-TEN-001
Parent: SRS-ROOT-001 v0.3
Version: 0.3
Status: Approved
Last Updated: 2026-04-25
Depends on: なし(最初に実装する基盤SRS)
依存される: SRS-RES-002, SRS-RES-004(store_settings の状態機械関連設定), SRS-RES-005, ほか Phase 1 全般
本書は SRS-ROOT-001 v0.3 に従う。
システム内に「店舗(Store)」エンティティを作成し、マルチテナントの起点となる初期データ(設定・プリセットロール・権限紐付け)を一括で生成する。
Phase 1 では管理画面・サインアップ UI は持たず、seed スクリプト(CLI)で店舗を作成する。これにより認証フローの設計負債を抱えずに済み、サインアップ画面は Phase 2 以降で丁寧に作れる。
本SRSはあわせて Phase 0 scaffold で広く配られていた app ロールの DEFAULT PRIVILEGES を default-deny に絞る基盤修正 を同梱する(親SRS §7.1 と整合させるため)。
2. ユーザーストーリー
Section titled “2. ユーザーストーリー”- As a 開発者(自分), I want to seed スクリプトで店舗と初期データを一発で作成する, so that 他の全機能の開発に着手できる
- As a 将来のサロンオーナー, I want to 店舗を作成したら必要な設定とロールが揃っている, so that すぐに運用を始められる
3. ユースケース
Section titled “3. ユースケース”3.1 主シナリオ(seed スクリプトによる店舗作成)
Section titled “3.1 主シナリオ(seed スクリプトによる店舗作成)”- 開発者が
docker compose run --rm api bun run db:seedを実行 - seed スクリプトが環境変数から店舗の入力(
SEED_STORE_NAME,SEED_STORE_SLUG,SEED_STORE_TIMEZONE?)を読み込む - Step 1: permission UPSERT(同一スクリプト内、
PERMISSIONS定数から) - Step 2: 1 トランザクションで以下を作成:
a.
slugで既存 store を検索 → あればスキップ(冪等) b.store(UUIDv7 + Base64url 11文字のcodeを自動生成) c.store_settings(デフォルト値で 1 行) d. プリセットrole× 4(owner / manager / staff / receptionist)、各 UUIDv7 e.role_permission(PRESET_ROLE_PERMISSIONS× 各 role の初期紐付け、store_id冗長保持) - トランザクション成功 → 作成した
store.id,store.slug,store.codeをコンソールに出力
3.2 代替フロー
Section titled “3.2 代替フロー”- AF-1. 既存店舗がある状態で再実行(冪等):
slugで既存判定 → 何もせず終了。store.codeは乱数なので冪等キーには使えず、slugを使う - AF-2. 複数店舗の作成: 環境変数を変えて複数回実行(Phase 1 では同時複数作成は対応しない)
3.3 例外フロー
Section titled “3.3 例外フロー”- EF-1. DB 未起動 / 接続エラー: seed スクリプトがエラーメッセージを出して exit 1
- EF-2. permission マスタ未投入: Step 1 で UPSERT するため発生しない(同一スクリプト内で順序保証)
- EF-3. store.code 衝突(天文学的確率): UNIQUE 制約違反 → リトライ(最大 3 回、別の code を再生成)。3 回失敗したら exit 1
- EF-4. slug バリデーション違反:
^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$を満たさない場合 exit 1 - EF-5. timezone バリデーション違反:
Asia/Tokyo以外 → exit 1(Phase 1 はAsia/Tokyoのみ)
4. UI 仕様
Section titled “4. UI 仕様”Phase 1 ではなし。店舗作成は CLI のみ。
将来的に管理画面で店舗設定を編集する UI が必要になるが、それは別 SRS(仮: SRS-TEN-004)で扱う。
5. API 仕様
Section titled “5. API 仕様”Phase 1 では店舗作成 API は公開しない。seed スクリプトが packages/db を直接呼ぶ。
5.1 将来の API(参考、Phase 1 スコープ外)
Section titled “5.1 将来の API(参考、Phase 1 スコープ外)”| Method | Path | 用途 | 備考 |
|---|---|---|---|
| POST | /api/admin/stores | 店舗作成 | Phase 2 以降、サインアップ連携時 |
| GET | /api/admin/stores/:storeId | 店舗情報取得 | Phase 1 後半で追加検討 |
| PATCH | /api/admin/stores/:storeId/settings | 店舗設定更新 | Phase 1 後半で追加検討 |
5.2 seed スクリプトの入力インターフェース
Section titled “5.2 seed スクリプトの入力インターフェース”環境変数で受ける(最軽量、複数店舗対応はYAGNI):
| 変数名 | 必須 | デフォルト | 説明 |
|---|---|---|---|
SEED_STORE_NAME | ✅ | — | 店舗名(例: "Hair Salon Tokyo") |
SEED_STORE_SLUG | ✅ | — | URL用slug。グローバルUNIQUE。冪等キーとして使用。^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$ |
SEED_STORE_TIMEZONE | — | Asia/Tokyo | IANAタイムゾーン名。Phase 1 は Asia/Tokyo のみ許可 |
DATABASE_URL_MIGRATOR | ✅ | — | superuser 接続文字列(既存の bootstrap と同じ) |
将来 JSON ファイル入力に切り替える場合は packages/db/src/seed/store.ts の入力 IF を変えるだけ(ロジック非依存)。
6. データモデル影響
Section titled “6. データモデル影響”6.1 スキーマ
Section titled “6.1 スキーマ”store(業務実体、UUIDv7)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
id | uuid | PK | UUIDv7、アプリ層で生成 |
slug | varchar(50) | UNIQUE, NOT NULL, CHECK | グローバル一意のURL slug。^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$ を DB CHECK で強制。冪等seedキー兼公開URL slug(親SRS §7.2.3) |
code | varchar(11) | UNIQUE, NOT NULL | Base64url 11 文字(8 バイト → Base64url)。乱数の短い識別子。冪等性には使わない(衝突時リトライで別値が生成されるため) |
name | varchar(200) | NOT NULL | 店舗名 |
timezone | varchar(50) | NOT NULL, DEFAULT 'Asia/Tokyo' | IANA タイムゾーン名。Phase 1 は Asia/Tokyo のみアプリ層で許可 |
plan | varchar(20) | NOT NULL, DEFAULT 'free' | 'free' | 'standard' | 'premium'。Phase 1 は 'free' 固定 |
created_at | timestamptz | NOT NULL, DEFAULT now() | |
updated_at | timestamptz | NOT NULL, DEFAULT now() |
store_settings(店舗設定、1:1)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
store_id | uuid | PK, FK → store(id) | |
reservation_slot_minutes | smallint | NOT NULL, DEFAULT 30 | 予約枠の刻み(5 / 10 / 15 / 30) |
max_concurrent_reservations_per_staff | smallint | NOT NULL, DEFAULT 1 | スタッフ並列予約上限(SRS-RES-005 §6.4) |
tentative_expire_hours | smallint | NOT NULL, DEFAULT 24 | 仮予約の自動失効までの時間(時間単位)。SRS-RES-004 §7.6 |
no_show_grace_minutes | smallint | NOT NULL, DEFAULT 30 | no_show マーク可能になる予約開始時刻からの猶予(分)。SRS-RES-004 §7.3 (†1) |
customer_required_fields | jsonb | NOT NULL, DEFAULT '["name"]' | 顧客登録の必須フィールド。Phase 1 は固定(UI 編集不可) |
cancel_policy | jsonb | NOT NULL, DEFAULT '{}' | キャンセルポリシー。Phase 2 以降で構造化 |
updated_at | timestamptz | NOT NULL, DEFAULT now() |
permission(システム共通マスタ、bigserial)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
id | bigserial | PK | |
key | varchar(100) | UNIQUE, NOT NULL | {scope}:{resource}:{action} 形式 |
description | text | NOT NULL DEFAULT '' | 人間向け説明 |
created_at | timestamptz | NOT NULL, DEFAULT now() |
親 SRS §7.7.2 の例外:
permissionは全テナント共通マスタのためstore_idを持たない。RLS なし。appロールにはSELECTのみ GRANT(INSERT/UPDATE/DELETE は migrator のみ)。
role(店舗スコープ、UUIDv7)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
id | uuid | PK | UUIDv7 |
store_id | uuid | FK → store(id), NOT NULL | |
key | varchar(50) | NOT NULL | owner / manager / staff / receptionist |
name | varchar(100) | NOT NULL | 表示名(例: "オーナー") |
is_preset | boolean | NOT NULL, DEFAULT true | プリセットロールか |
created_at | timestamptz | NOT NULL, DEFAULT now() | |
| UNIQUE | (store_id, key) | 同一店舗内でキーは一意 | |
| UNIQUE | (store_id, id) | role_permission の複合FK参照先(Postgresの複合FK要件) |
role_permission(中間テーブル、独立IDなし、複合PK)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
store_id | uuid | NOT NULL | role.store_id と一致(複合FKで保証) |
role_id | uuid | NOT NULL | |
permission_id | bigint | NOT NULL | |
| PRIMARY KEY | (role_id, permission_id) | 独立IDなし。親SRS §7.2.2「中間テーブルは複合PKで最小構成を優先」 | |
| FK | (store_id, role_id) → role(store_id, id) | role 側の UNIQUE(store_id, id) を参照する複合FK | |
| FK | (permission_id) → permission(id) | permission は store_id を持たないため単独FK |
store_idは冗長保持(親SRS §7.1.1「全テーブルstore_id例外なし」)。複合FKでrole.store_idとの整合をDB保証する。RLSもこのカラムで効く。
6.2 マイグレーション計画
Section titled “6.2 マイグレーション計画”BC-TEN 初回マイグレーションで以下を 2 ファイルに分割して実施する:
0001_initial.sql:drizzle-kitgenerate出力(テーブル定義、UNIQUE、複合FK)0002_enable_rls.sql:drizzle-kitgenerate --customで空ファイルを作り、手書きで RLS + GRANT を記述
分割理由:drizzle-kit はスキーマ DSL から RLS/GRANT を生成できないため。手書き SQL は journal で追跡される(drizzle-kit migrate 連続適用)。
6.2.1 0002_enable_rls.sql の内容(要点)
Section titled “6.2.1 0002_enable_rls.sql の内容(要点)”-- storeALTER TABLE store ENABLE ROW LEVEL SECURITY;ALTER TABLE store FORCE ROW LEVEL SECURITY;CREATE POLICY tenant_isolation_select ON store FOR SELECT USING (id = current_setting('app.current_store_id')::uuid);CREATE POLICY tenant_isolation_modify ON store FOR ALL USING (id = current_setting('app.current_store_id')::uuid) WITH CHECK (id = current_setting('app.current_store_id')::uuid);GRANT SELECT, INSERT, UPDATE, DELETE ON store TO app;
-- store_settings (store_id ベース)ALTER TABLE store_settings ENABLE ROW LEVEL SECURITY;ALTER TABLE store_settings FORCE ROW LEVEL SECURITY;CREATE POLICY tenant_isolation_select ON store_settings FOR SELECT USING (store_id = current_setting('app.current_store_id')::uuid);CREATE POLICY tenant_isolation_modify ON store_settings FOR ALL USING (store_id = current_setting('app.current_store_id')::uuid) WITH CHECK (store_id = current_setting('app.current_store_id')::uuid);GRANT SELECT, INSERT, UPDATE, DELETE ON store_settings TO app;
-- role (store_id ベース)-- 同様
-- role_permission (store_id ベース、冗長保持カラム)-- 同様
-- permission: RLS なし、SELECTのみGRANT SELECT ON permission TO app;GRANT USAGE, SELECT ON SEQUENCE permission_id_seq TO app;6.2.2 default-deny への bootstrap 修正
Section titled “6.2.2 default-deny への bootstrap 修正”infra/sql/bootstrap/001_roles_and_extensions.sql を以下に変更:
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app;-- INSERT/UPDATE/DELETE はテーブル作成時に明示 GRANT する(migration の責務)ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO app;infra/sql/policies/tenant.sql テンプレに「テーブル作成時に必ず GRANT SELECT, INSERT, UPDATE, DELETE ON {{table}} TO app; を明示する」旨を追記する。
6.3 seed 実行フロー
Section titled “6.3 seed 実行フロー”db:seed (DATABASE_URL_MIGRATOR = postgres superuser で接続)├─ env validate (SEED_STORE_NAME, SEED_STORE_SLUG, SEED_STORE_TIMEZONE?)├─ Step 1: permission UPSERT(packages/auth の PERMISSIONS 定数から、ON CONFLICT(key) DO UPDATE SET description)└─ Step 2: BEGIN ├─ SELECT 1 FROM store WHERE slug = $1 → 既存ならスキップして COMMIT ├─ generateStoreCode() リトライループ(最大3回) ├─ INSERT store ├─ INSERT store_settings ├─ INSERT role × 4 (UUIDv7 アプリ層生成) └─ INSERT role_permission(PRESET_ROLE_PERMISSIONS から、store_id 冗長保持)COMMIT → store.id, store.slug, store.code を stdout に出力seed は postgres superuser で実行する。RLS が FORCE されていても superuser はバイパスする(Postgres 仕様)。
7. 業務ルール
Section titled “7. 業務ルール”7.1 store.code の生成
Section titled “7.1 store.code の生成”import { randomBytes } from 'node:crypto'
export function generateStoreCode(): string { return randomBytes(8).toString('base64url').slice(0, 11)}- 8 バイト(64bit)の暗号論的乱数を Base64url エンコード → 11 文字
- 文字種:
A-Za-z0-9_-(64 種、大文字小文字区別) - キースペース: 2^64 ≈ 1.8 × 10^19(衝突はまず起きない)
- DB の UNIQUE 制約で最終保証、衝突時はリトライ(最大 3 回)
7.2 Permission キー初期セット(TEN-001 範囲)
Section titled “7.2 Permission キー初期セット(TEN-001 範囲)”TEN-001 が責務を持つキーのみを定義する。後続SRS(TEN-002, MST-, RES- 等)が新 permission を追加するときは、親SRS §7.7.7 の backfill 規約に従う。
export const PERMISSIONS = { ADMIN_STORE_READ: 'admin:store:read', ADMIN_STORE_UPDATE: 'admin:store:update', ADMIN_STORE_SETTINGS_READ: 'admin:store_settings:read', ADMIN_STORE_SETTINGS_UPDATE:'admin:store_settings:update', ADMIN_ROLE_READ: 'admin:role:read',} as constPRESET_ROLE_PERMISSIONS(TEN-001 範囲のみ):
| Role | Permissions |
|---|---|
owner | 全部(5件) |
manager | 全部(5件、TEN-001 範囲では owner と同集合) |
staff | admin:store:read, admin:store_settings:read, admin:role:read |
receptionist | admin:store:read, admin:store_settings:read, admin:role:read |
注: TEN-001 の permission 範囲では
managerとownerの差、staffとreceptionistの差は表現できない。これは仕様の不足ではなく範囲の問題で、後続SRSで追加される permission(admin:operator:invite,admin:reservation:cancel,admin:register:operate等)によって初めて差が出る。SRS-TEN-002 以降でPRESET_ROLE_PERMISSIONSを分化させる前提。
7.3 store_settings のデフォルト値
Section titled “7.3 store_settings のデフォルト値”| 設定 | デフォルト | 根拠 |
|---|---|---|
reservation_slot_minutes | 30 | 美容室の一般的な刻み |
max_concurrent_reservations_per_staff | 1 | 安全側。並列施術を許可する店舗は手動で変更 |
tentative_expire_hours | 24 | 一般的な「翌日まで返事がなければ自動取消」運用に合わせる |
no_show_grace_minutes | 30 | 開始時刻から30分過ぎたら no-show マークを許可(早押し誤操作の防止) |
customer_required_fields | ["name"] | 親 SRS §7.13 |
cancel_policy | {} | Phase 2 以降で構造化 |
7.4 timezone のバリデーション
Section titled “7.4 timezone のバリデーション”Phase 1 では Asia/Tokyo のみ許可:
export const ALLOWED_TIMEZONES = ['Asia/Tokyo'] as constexport type StoreTimezone = (typeof ALLOWED_TIMEZONES)[number]将来の多タイムゾーン対応時にこの配列を拡張するだけ。
7.5 plan の扱い
Section titled “7.5 plan の扱い”Phase 1 では 'free' 固定。カラムのみ置き、課金ロジックは実装しない:
export const STORE_PLANS = ['free', 'standard', 'premium'] as constexport type StorePlan = (typeof STORE_PLANS)[number]7.6 slug のバリデーション
Section titled “7.6 slug のバリデーション”正規表現 ^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$:
- 小文字英数字とハイフンのみ(公開URL のため)
- 長さ 3〜50(先頭末尾のハイフン禁止)
- アプリ層(zod or 自作 validator)+ DB CHECK の二重ガード
7.7 削除(deleted_at)方針
Section titled “7.7 削除(deleted_at)方針”TEN-001 範囲のテーブル(store, store_settings, permission, role, role_permission)には deleted_at を追加しない。
- 親SRS §7.5「物理削除はしない、必要な箇所のみ
deleted_at」に従い、Phase 1 では削除運用を想定しない - 店舗の論理削除が必要になった時点(Phase 3+)で別 migration で追加する
- preset role の論理削除は preset 定義そのものの変更なので別の運用(permission 粒度変更規約 §7.7.5)で扱う
8. 非機能要件
Section titled “8. 非機能要件”親 SRS §6 に従う。特記事項:
- seed スクリプトの実行時間: 1 店舗あたり 1 秒以内(permission UPSERT + store 作成)
- 冪等性: 同じ
SEED_STORE_SLUGで再実行しても安全(既存ならスキップ)
9. セキュリティ・認可
Section titled “9. セキュリティ・認可”9.1 Phase 1 での認可
Section titled “9.1 Phase 1 での認可”店舗作成は seed スクリプト(postgres superuser)で行うため、アプリ層の認可チェックは不要。
将来 API 化する場合の permission キー(参考):
| key | 用途 | 備考 |
|---|---|---|
system:store:create | 店舗作成 | システム管理者のみ。通常のプリセットロールには付与しない |
9.2 RLS
Section titled “9.2 RLS”§6.2 参照。store テーブルの RLS ポリシーは id = current_setting('app.current_store_id')::uuid、その他は store_id = ...。permission のみ RLS なし(共通マスタ)。
seed 実行時は postgres superuser なので RLS をバイパスする(FORCE は非 superuser に対して適用される)。
9.3 監査
Section titled “9.3 監査”seed の実行は親SRS §7.8.1 に従い operator_action_log 記録対象外(CLI ログで履歴を残す)。
9.4 GRANT 規律
Section titled “9.4 GRANT 規律”親SRS §7.1 を踏まえ、本SRSは bootstrap の DEFAULT PRIVILEGES を default-deny(SELECT のみ)に絞る修正を同梱する(§6.2.2)。各テーブル migration で GRANT SELECT, INSERT, UPDATE, DELETE ON {{table}} TO app; を明示する規律を infra/sql/policies/tenant.sql テンプレに追加する。
10. 受け入れ基準(Given-When-Then)
Section titled “10. 受け入れ基準(Given-When-Then)”- GWT-1 基本作成: Given DB が空 / When seed を実行 / Then
store1 行、store_settings1 行、role4 行、role_permission14 行(owner 5 + manager 5 + staff 3 + receptionist 3 = 16… ※ permission 構成変更時はマトリクス再計算)が作成される - GWT-2 store.code 形式: Given seed 実行 / When 作成された
store.codeを確認 / Then Base64url 11 文字(/^[A-Za-z0-9_-]{11}$/)である - GWT-3 store.slug 形式: Given
SEED_STORE_SLUG=my-shop-1/ When seed 実行 / Then DB のslugがそのまま保存される - GWT-4 冪等性(slug ベース): Given 同じ slug で seed 済み / When 同じ slug で再実行 / Then 既存スキップ、データ重複なし
- GWT-5 1 トランザクション: Given seed 実行中に途中エラー / When ロールバック / Then
storeもroleも作成されていない - GWT-6 RLS テナント分離(store): Given 店舗 A, B が seed 済み / When
appユーザーでwithTenant(A.id)内でSELECT * FROM store/ Then 店舗 A のみ返る - GWT-7 RLS role 分離: Given 店舗 A, B が seed 済み / When
appユーザーでwithTenant(A.id)内でSELECT * FROM role/ Then 店舗 A のロール 4 件のみ返る - GWT-8 permission はテナント非依存: Given 店舗 A を
current_store_idに設定 / WhenSELECT * FROM permission/ Then 全 permission が返る(RLS なし) - GWT-9 timezone バリデーション: Given
SEED_STORE_TIMEZONE=America/New_York/ When seed 実行 / Then exit 1(Phase 1 はAsia/Tokyoのみ) - GWT-10 slug バリデーション: Given
SEED_STORE_SLUG=Invalid_Slug/ When seed 実行 / Then exit 1 - GWT-11 store.code 衝突リトライ: Given UNIQUE 制約違反が発生 / When 生成ロジック / Then 別の code で最大 3 回リトライし、成功する
- GWT-12 RLS current_store_id 未設定: Given
appユーザーでcurrent_store_idを設定せずにSELECT * FROM store/ Then エラー(current_settingがデフォルト値なし)または 0 行 - GWT-13 default-deny 確認: Given bootstrap 実行後 / When 新規テーブルを migration なしで作成(テスト用) / Then
appロールからINSERT/UPDATE/DELETEができない(SELECT のみ) - GWT-14 全 tenant table RLS 検査: Given migration 完了後 / When
pg_classから RLS ENABLE のテーブル列挙 / Thenpermission以外の本SRS追加テーブルすべてが含まれる、各テーブルにtenant_isolation_selectとtenant_isolation_modifyポリシーが存在する
11. テスト計画
Section titled “11. テスト計画”- Unit (
packages/domain/test/store/):generateStoreCode()が 11 文字の Base64url を返す- timezone validator(許可 / 拒否)
- plan validator
- slug validator(正規表現)
- Integration (
packages/db/test/):- GWT-1〜14 を網羅
- Compose ベース runner(
docker-compose.test.ymlで test-db を別ポートで立てる、もしくは既存 db に分離スキーマ) - RLS 統合検査(
pg_class/pg_policiesメタデータ走査) withTenant()経由のテナント分離検証(SET LOCALがトランザクション境界で正しく効くこと)
- Contract: Phase 1 では API がないため不要
12. 関連ジョブ(graphile-worker)
Section titled “12. 関連ジョブ(graphile-worker)”Phase 1 ではなし。
13. Open Questions
Section titled “13. Open Questions”| # | 内容 | 結論 |
|---|---|---|
role_permission に store_id を冗長に持たせるべきか | Closed v0.2: 持たせる。bigserial id を廃止し複合PK (role_id, permission_id) + 複合FK (store_id, role_id) → role(store_id, id) で整合をDB保証 | |
| seed 入力形式 | Closed v0.2: 環境変数(SEED_STORE_NAME 必須、SEED_STORE_SLUG 必須=冪等キー、SEED_STORE_TIMEZONE 任意) |
14. 変更履歴
Section titled “14. 変更履歴”| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-04-23 | yudai | 初版起票(Draft)。store.code は Base64url 11 文字、Phase 1 は seed スクリプトのみ(API/UI なし)。店舗作成 + store_settings + プリセット role + role_permission を 1 トランザクションで生成する方式 |
| 0.2 | 2026-04-25 | yudai | コードレビュー反映。store.slug を NOT NULL UNIQUE で追加(DB CHECK + アプリ正規表現)し冪等キーに採用。role_permission を bigserial 廃止 → 複合PK (role_id, permission_id) + store_id 冗長保持 + 複合FK 化(role 側 UNIQUE(store_id, id) 追加)。permission 初期セットを TEN-001 範囲の5件に限定し、後続SRSでの追加は親SRS §7.7.7 backfill 規約に従う。bootstrap の DEFAULT PRIVILEGES を default-deny に修正する基盤改修を同梱(§6.2.2)。drizzle-kit --custom で 0002_enable_rls.sql を journal 連携。seed/bootstrap は親SRS §7.8.1 に従い監査例外。deleted_at は本SRS範囲のテーブルでは追加しない方針を §7.7 に明記。受け入れ基準を GWT-1〜14 に拡張(slug バリデーション、default-deny、RLS メタデータ検査、current_store_id 未設定時の挙動を追加)。OQ-TEN-001-01/02 を Closed。Status を Approved に |
| 0.3 | 2026-04-25 | yudai | SRS-RES-004 v0.2 と同期改訂。store_settings に tentative_expire_hours(既定 24)と no_show_grace_minutes(既定 30)を追加(§6.1, §7.3)。SRS-RES-004 の状態機械(仮予約自動失効 / no_show 猶予)から参照される。実装は別 migration(BC-RES 連動)で追加 |