Crowi QA (Bounded DevTools Charter Runner)
SkillWeb & browsingcrowi-qa lets your AI quality-check critical user flows in a real browser. Each flow table is expanded into nine focused charters and run with limits on operations, retries, and time. It handles visual and state-dependent checks and production-build smoke tests while leaving your existing regression tests untouched.
Available today. Use it from your connected AI after setup.
No other account needed.
Add the skill, then ask your AI to run a bounded QA pass over a critical flow you care about. It complements your existing regression tests rather than replacing them.
Then ask your AI: use the Crowi QA (Bounded DevTools Charter Runner) skill
What your AI can do with it
- Test critical user flows in a real browser
- Run checks through nine focused charters per flow table
- Cap operations, retries, and time for each check
- Verify visual and state-dependent behavior
- Smoke test standalone production builds
- Drive checks with Chrome DevTools, falling back to Claude in Chrome when needed
What this skill tells your AI
The instructions your AI receives, as published by crowi/crowi in .claude/skills/crowi-qa/SKILL.md and read by ahel’s review.
これは何か / 何ではないか
crowi-qa は 有限のブラウザ探索 QA を行う skill。.feature-state/specs/ feature-crowi-qa.md が正本設計で、本ファイルはそれを実行手順に落としたもの
(設計判断そのものを変えたい場合は spec を先に直す)。
- やること: 対象 worktree の proxy URL (anchor+3) に対し、9 個の有限
チャーター (§2) を順に実ブラウザで駆動し、証跡 (
.reviews/qa/<run-id>/) と findings を残す。--prod-buildで standalone ビルドの成立確認もする。 - やらないこと (
packages/e2eの責務との切り分け):packages/e2eの書き換え・Playwright テストの追加はしない。決定的回帰は E2E の役割のまま。- 新しいブラウザ自動化ライブラリを導入しない。chrome-devtools MCP と claude-in-chrome の既存ツールだけを使う。
- 永続的な QA 用 DB・製品側の QA 状態モデルを追加しない。
.claude/agents/feature-planner.md:84-90のクリティカルフロー表そのものを 拡張しない。人間がこの表を明示的に変えない限り、チャーターを増減しない。- リリースの自動 merge / tag / push はしない。
crowi-releaseの「merge / tag / push / publish はすべてユーザーの明示承認後」という鉄則も変えない。 packages/**の製品コードを変更しない。QA の結果バグが見つかった場合、 直すかどうかは fix-or-drop の対象だが、直す作業自体は呼び出し文脈 (人間 /integrate-worktree/crowi-release) が行う。- QA 用テストアカウントを自動登録しない (事前に用意された認証情報を要求する)。
- git push をしない (証跡はすべて
.reviews/qa/のローカル state。commit も しない —.reviews/は既存の gitignore 規約でカバーされる)。
起動構文
/crowi-qa # target 省略 = 現在の worktree
/crowi-qa main # main worktree
/crowi-qa <worktree-key> # 例: /crowi-qa admin-security
/crowi-qa --url https://staging.example.com # 明示 URL (registry を経由しない)
/crowi-qa <target> --prod-build # standalone ビルドスモークも実行
/crowi-qa <target> --charters 1,2,5 # 交差判定した charter だけに絞る (省略時は全 9)
/crowi-qa <target> --i-understand-destructive # 非ローカル --url で mutation charter を許可
target の解決順:
main(文字列そのまま)- worktree key (
scripts/dev-ports.mjs:45のnormalizeWorktreeKeyと同じ 正規化 — worktree ディレクトリ basename のcrowi-prefix を外したもの。crowi自体はmainに特殊化) --url <url>明示指定 (registry を経由しない。§1.7 のガードが適用される)- 省略時 = 現在の worktree (
git rev-parse --show-toplevelからnormalizeWorktreeKeyで導出)
--charters は選択的フック (integrate-worktree の交差判定、§2 相当) 用の
省略可能フラグで、指定チャーター番号 (カンマ区切り) だけを実行する。省略時は
9 チャーター全部 (full QA)。
§1. Target 解決 (registry は read-only)
1.1 anchor / proxy の解決
-
~/.crowi-dev-ports.json(scripts/dev-ports.mjs:32のDEFAULT_REGISTRY_PATH) を読み、portsForAnchor(anchor)(scripts/dev-ports.mjs:57) で{ api, web, site, proxy }を得る (proxy = anchor + 3)。実装コードを import せず、同等の内容を Bash /node -eで参照する:node -e " const raw = require('fs').readFileSync(process.env.HOME + '/.crowi-dev-ports.json', 'utf8'); const registry = JSON.parse(raw); const anchor = registry['<key>']; if (anchor === undefined) { console.error('not running'); process.exit(1); } console.log(JSON.stringify({ api: anchor, web: anchor + 1, site: anchor + 2, proxy: anchor + 3 })); "registry を 書き込まない —
allocateAnchor(scripts/dev-ports.mjs:223) は絶対に呼ばない。対象 worktree のpnpm devがまだ起動していない (keyが registry に無い) 場合は、新しいアンカーを推測で採番せずblocked: <target> is not running (start 'pnpm dev' in that worktree first)として終了する。 -
proxy 以外へは QA しない: raw web port (anchor+1) には直接アクセスしない。
/collab//presence//notificationsの WS は同一オリジン proxy (anchor+3) 経由でしか成立しない (packages/web/src/lib/resolve-ws-url.tsの doc comment、scripts/dev-caddy.mjsのWS_NAMESPACES)。proxy 以外への QA は WS namespace を検証できないため許可しない。
1.2 stale registry entry の拒否
readRegistry (scripts/dev-ports.mjs:67) は gw end 済み・削除済み
worktree の残留 key をそのまま返す (pruning は組み込まれていない)。target
解決の一部として毎回:
git worktree list --porcelain | awk '/^worktree /{print $2}'
の各パスを normalizeWorktreeKey と同じ規則 (basename の crowi- prefix を
外す。crowi は main) で正規化し、解決対象 key がこの集合に含まれることを
確認する。含まれない場合は
blocked: stale registry entry (worktree '<key>' not found in 'git worktree list' — the worktree has likely been closed)
として中断する (registry への pruning 書き込みはしない — read-only を保つ)。
1.3 疎通確認
GET <proxy>/api/app/info が 200 を返すことを健全性の最低条件にする
(packages/e2e/playwright.config.ts の webServer readiness probe と同じ
エンドポイント)。200 でなければ blocked: proxy not responding at <proxy url>。
1.4 proxy identity 検証 (mutation を伴う charter のみ必須)
/api/app/info (packages/api/src/hono/handlers/app.ts) は title /
version / capabilities のみを返し、worktree key・cwd・branch・git sha を
含まない。gw end 後に別プロセスが同じ proxy ポートを再利用した、等の
ポート再利用ケースを 200 応答だけでは検出できない。したがって
mutation を伴う charter (#3・#4・#5・#9、認証 charter の mutating
サブフロー、--prod-build) を開始する前に:
PID=$(lsof -iTCP:<proxyPort> -sTCP:LISTEN -t)
CWD=$(lsof -a -p "$PID" -d cwd -Fn | sed -n 's/^n//p')
# $CWD が解決対象 worktree のディレクトリ配下であることを確認
一致しない、または lsof が使えない環境では
blocked: cannot verify proxy process identity for <target>
として該当 charter (または --prod-build) をスキップする。読み取り専用
charter (認証の login/logout/session サブフロー・検索・通知一覧表示・collab
の疎通確認のみ) はこの検証を必須としない — mutation のリスクが無いため。
1.5 dev infra チェック (Mongo/Redis)
packages/e2e/src/preflight.ts と同じ発想で、対象 worktree の Mongo
(27017) / Redis (6379) への TCP 到達性を確認する (DB 分離時は接続先ホスト自体は
同じ、db 名だけが違うので TCP 到達性チェックはポート単位でよい)。落ちていれば
blocked: dev infra down (docker compose up -d が必要)
として 全チャーターを実行せずに終了する。これは crowi-complete-feature
の infra-down 時の扱い (.claude/skills/crowi-complete-feature/SKILL.md —
fail 扱いにせず blocked として signal を立てず報告) と同じ方針。
1.6 active backend の preflight (API レベルの canary)
対象 worktree のランナー projectDir (dev は常に apps/crowi-runner) の
crowi.config.json を読み、storage.driver / search.driver を確認する —
これは「external driver かどうかの判定」だけに使い、接続先 URL・認証情報は
読まない (crowi.config.json にはそもそも無く、実体は Mongo Config 側に
あり暗号化される場合もある。admin API も hasValue に redact して返す —
packages/api/src/hono/handlers/admin/plugins.ts)。ES の _cluster/health
や S3 バケットへの直接到達確認はしない。
external driver を使う charter は、charter 自身の最初の 1 操作を canary として使う:
-
検索 charter (#6): 最初の検索クエリを実行する。
crowi.getSearcher()が未登録 (ES URL 未設定) ならSEARCH_UNAVAILABLE_BODY付き 503 (packages/api/src/hono/handlers/search.ts) が返る。この 503 を見た時点で 残りのバジェットを使わず即座にblocked: search backend unreachable (503)として charter を終える。 -
添付・アップロード charter (#9): 最初のアップロード操作を canary として 実行する。S3 の bucket 未設定は
requireBucket(packages/plugin-storage-aws-s3/src/index.ts) の例外として現れるが、 ハンドラ側は他の失敗と区別せず汎用の 500 (UPLOAD_FAILED/INTERNAL_ERROR_BODY—packages/api/src/hono/handlers/attachment.ts) を 返すため、検索のような一意な信号が無い。最初の操作が 5xx で失敗した場合は 「backend 未接続」と決めつけず、blocked: attachment charter first action failed (<status>, see network.log — ambiguous: storage backend unreachable or product bug)として severity
highの finding として記録し、残りのバジェットを 使わずに charter を終える (製品バグの可能性を握り潰さない)。 -
ローカル driver (storage-local / search-mongo 等) しか要求しない構成では、 この canary 分岐は発生せず charter は通常どおり全操作を実行する。
1.7 --url の扱い (destructive ガード)
--url 指定時は registry を経由せず、そのまま疎通確認 (1.3) のみ行う。
worktree 検証 (1.2)・proxy identity 検証 (1.4)・active backend preflight
(1.6) は可能な範囲で行い、できない部分は environment.json に
「worktree 検証: skipped」と明記する。
ローカル判定の定義 (dev-port registry にホスト名は保持されていないため read-only な導出をする): 以下のいずれかに一致すれば「ローカル」:
localhost/127.0.0.1かつポート番号が 解決済み anchor からportsForAnchorで導出される 4 ポート (api/web/site/proxy) のいずれか に一致する。resolveTailscaleHostname()(scripts/dev-ports.mjs:407—tailscale status --jsonから解決) が 実際に解決できた tailscale hostname に 一致する。
node -e "
try {
const out = require('child_process').execFileSync('tailscale', ['status', '--json'], { encoding: 'utf8' });
console.log(JSON.parse(out)?.Self?.DNSName?.replace(/\.\$/, '') ?? '');
} catch { console.log(''); }
"
どちらにも一致しない --url (社内ステージング・本番相当ドメイン等) は
非ローカルと判定する。
非ローカル判定の --url は、mutation を伴うチャーター (#3・#4・#5・#9、
認証チャーターの mutating サブフロー) と --prod-build を
blocked: destructive charter refused on non-local --url (pass --i-understand-destructive to override)
としてスキップする。--i-understand-destructive を明示した場合のみ実行する
(縮退実行や既定 override はしない)。読み取り専用チャーター (認証の
login/logout/session 遷移のみ・検索・通知一覧表示・collab の疎通確認のみ) は
--url のホスト制限を受けない。
§2. 9 チャーターと対応パス表
.claude/agents/feature-planner.md:84-90 のクリティカルフロー表を唯一の
正本として展開する (表そのものは拡張しない)。「対応パス」列は
integrate-worktree の選択的フック判定 (本節) に使う交差判定表:
| # | charter | 既存 E2E カバレッジ (サブフロー単位) | 対応パス (交差判定用) |
|---|---|---|---|
| 1 | 認証 (§3 で shared-DB 読み取り専用 / isolated-DB mutating の 2 層に分離) | partial: login/logout/session 遷移は auth-state.spec.ts がカバー (shared DB・読み取り専用)。installer 経由の admin 作成は onboarding.setup.ts が UI 経由で一度だけ通す happy path のみ。oauth / password reset / activation / email change は未カバー | packages/api/src/hono/middleware/auth.ts, packages/api/src/hono/handlers/{tokenAuth,oauth,passwordReset,activation,emailChange,me,installer}.ts, packages/web/src/app/(public)/**, packages/web/src/app/(auth)/oauth/**, packages/web/src/lib/{use-auth,api-client}.ts |
| 2 | collab (+ presence) | partial: 2 窓間 edit propagation は collab.spec.ts がカバー。presence (viewer 一覧・indicator) は未カバー | packages/api/src/collab/**, packages/api/src/presence/**, packages/api/src/hono/handlers/{page-collab,presence}.ts, packages/web/src/components/editor/**, packages/web/src/lib/{use-collab-document,use-presence,resolve-ws-url}.ts |
| 3 | ページ CRUD・rename・trash | partial: onboarding.setup.ts は API 経由 (createPageViaApi) で 1 ページを seed するのみ。UI 経由の CRUD/rename/trash は未保護 | packages/api/src/hono/handlers/{page,backlink,page-portalize-twin}.ts, packages/api/src/models/{page,revision,backlink}.ts, packages/api-contract/src/contracts/page.ts, packages/web/src/app/(auth)/[[...slug]], packages/web/src/app/(auth)/trash, packages/web/src/lib/{use-page,use-page-mutations,use-page-list,use-page-children}.ts |
| 4 | エディタ save・draft | なし | packages/api/src/hono/handlers/{draft,revision,page-preview}.ts, packages/api-contract/src/contracts/page-preview.ts, packages/web/src/app/(auth)/%5Fedit, packages/web/src/components/editor/**, packages/web/src/lib/{use-drafts,use-page-revisions}.ts |
| 5 | コメント | なし | packages/api/src/hono/handlers/comment.ts, packages/api/src/models/comment.ts, packages/api-contract/src/contracts/comment.ts, packages/web/src/lib/use-page-comments.ts, packages/web/src/components/page-comments/** |
| 6 | 検索 | なし | packages/api/src/hono/handlers/search.ts, packages/api-contract/src/contracts/search.ts, packages/web/src/app/(auth)/%5Fsearch, packages/web/src/lib/use-search.ts, packages/plugin-search-* |
| 7 | 通知 | なし | packages/api/src/notifications/**, packages/api/src/hono/handlers/notification.ts, packages/api-contract/src/contracts/notification.ts, packages/web/src/app/(auth)/%5Fnotifications, packages/web/src/lib/{use-notifications,use-notifications-socket,resolve-ws-url}.ts |
| 8 | 管理設定 (§3 で既定 read-only) | partial: onboarding.setup.ts が mail SMTP 送信元アドレス保存・ユーザー招待・招待受諾をカバー。セキュリティ設定・プラグイン設定 (mail 以外)・crypto 等は未保護 | packages/api/src/hono/handlers/admin/**, packages/api-contract/src/contracts/admin/**, packages/web/src/app/(admin)/** |
| 9 | 添付・アップロード | なし | packages/api/src/hono/handlers/attachment*.ts, packages/api/src/models/attachment.ts, packages/api-contract/src/contracts/attachment.ts, packages/web/src/app/(auth)/%5Fattachments, packages/web/src/lib/{use-attachments,use-attachment-usage}.ts |
横断 fanout パス (複数チャーターへ OR 条件で交差)
以下は単一チャーターへの割り当てだけでは交差判定が漏れるため、列記した すべてのチャーターを交差対象にする (表の割り当てに加えて OR 条件):
packages/web/src/lib/resolve-ws-url.ts—/collab//presence//notificationsが共有する WS URL 解決ロジック。charter 2・7 を 交差対象にする。packages/web/src/lib/api-client.ts,packages/web/src/lib/use-auth.ts(トークン取得・付与・refresh) — 認証済み API 呼び出しは全チャーターが この共有クライアント経由。charter 1〜9 すべてを交差対象にする。- ランタイム env 解決 (
window.__ENV注入元のレイアウト、NEXT_PUBLIC_*読み出し全般) — 同様に charter 1〜9 すべてを交差対象にする。 packages/api/src/util/fileUploader.ts(すべての put/get/delete をアクティブな storage driver に委譲する共有層) とpackages/plugin-storage-*/(実ドライバ 実装) — charter #9 の対応パスに追加する (表の #9 行はハンドラ/contract/ web レイヤーのみで、実際の読み書きを担うこの共有 storage 層が漏れていた)。
共有 runtime / proxy パス (個別割り当てを試みず全 9 charter = full QA)
以下は影響範囲が「どの charter に効くか」を OR 条件では判定しきれないほど 広く (全 namespace のルーティング・全ハンドラの登録・全アクティブ driver の 解決)、変更があれば charter を絞らず全 9 charter を対象にする:
packages/api/src/hono/index.ts—buildHonoAppが全 route / 全 admin サブリソースを登録する配線そのもの。packages/api/src/plugin/plugin-manager.ts— アクティブな storage / search driver の解決。packages/api/src/crowi/index.ts— boot sequence (Crowi.init全体)。scripts/dev-caddy.mjs— proxy の同一オリジンルーティング表 (API_HTTP_PATHS/pickProxyTarget)。ここが壊れると WS namespace を 含む全 charter の疎通そのものが壊れる。
§3. バジェットと incomplete / blocked の区別
charter 単位のデフォルトバジェット:
| 項目 | 上限 |
|---|---|
| ブラウザ操作 | 最大 10 回 |
| 失敗操作のリトライ | 最大 2 回 |
| 経過時間 (ソフト) | 5 分 |
| 経過時間 (ハード) | 8 分 |
上限到達時は incomplete (budget exhausted) としてそこまでの証跡を
確定し、粘らずに次の charter に進む。blocked (前提条件不足 — infra down /
資格情報なし / proxy identity 不一致 / 非ローカル --url 拒否 等) とは
明確に区別する:
blocked= 前提条件が満たせず charter を 開始できない、または途中で 前提が崩れた。incomplete= charter を実際に探索したが、バジェット (操作数/リトライ/ 時間) を使い切って 打ち切った。
サブフロー単位の E2E カバレッジ調整 (charter 全体を一括判定しない)
- smoke レベル (バジェットを半分程度に短縮): 認証 charter (#1) のうち
login/logout/session 遷移 (
auth-state.spec.tsがカバー)、collab charter (#2) のうち 2 窓 edit propagation (collab.spec.tsがカバー) のみ。視覚 / ログ / WS 証跡の確認に重点を置き、既存アサーションを再実装しない。 - 通常バジェット: 同じ charter 内でも installer 経由の admin 作成 (単発 happy path のみ検証済み)・oauth・password reset・activation・ email change・presence は E2E が守っていない (または単発 happy path に 留まる) ため通常バジェットで探索する。
- ページ CRUD・rename・trash (#3) / 管理設定 (#8): バジェットを縮小
しない。
onboarding.setup.tsがカバーする一部 (API 経由のページ seed、 mail SMTP 保存・ユーザー招待・招待受諾) の再確認は省略するが、それ以外 (UI 経由の CRUD/rename/trash、管理設定の他セクション) は未保護のフローと 同じ扱いで通常バジェットで探索する。
既存 E2E が守っているアサーションを crowi-qa が再実装することはない
(.feature-state/specs/dev-cycle-skills/04-e2e-targets.md のポイント
ポイント方針を踏襲)。
§4. 認証 charter (#1) の 2 層構成
4.1 shared-DB 読み取り専用サブフロー (既定・どの target でも実行可)
login / logout / session 遷移 (同一タブ切替・別タブ logout 伝搬・reload
保持)。環境変数 CROWI_QA_USER_EMAIL / CROWI_QA_USER_PASSWORD で受け取る
既存アカウントを使う。
4.2 isolated-DB 限定 mutating サブフロー
対象: POST /auth/register・POST /auth/activate・POST /auth/reset-password・PUT /me の email 変更申請 + POST /auth/confirm-email-change・POST /installer/createAdmin・oauth
authorize/device consent (いずれも User / Config / OAuth トークンレコードを
書き換える)。
-
既定 (共有 dev DB) では実行しない。共有 dev DB のユーザーレコードや installer 済み状態を書き換えると復元できない。
-
isolated DB (
dev.local.jsonでisolateDb: trueを宣言した worktree、または--prod-buildの per-run DB) でのみ実行してよい。 それ以外 (§1.7 の非ローカル--urlを含む) ではblocked: mutating auth subflow requires isolated DBとしてこれらのサブフローだけをスキップし、読み取り専用サブフロー (4.1) は 通常どおり実行する。isolated DB 側の資格情報は
CROWI_QA_ISOLATED_ADMIN_EMAIL/CROWI_QA_ISOLATED_ADMIN_PASSWORDで受け取る (自動登録はしない — 未設定 ならblocked: no credentials (isolated DB)としてスキップ)。 -
Mailpit の稼働チェックが前提条件: password reset の forgot-password は mail を fire-and-forget して常に 200 を返す・register の activation mail・email change の確認 mail はいずれも mail 内 token リンクを踏まないと 完了しない。SMTP driver は host 未設定で例外を投げるため、mutating サブフローを始める前に
packages/e2e/src/preflight.tsのassertMailpitHttpと同じ検査 (SMTP 1025 / HTTP 8025 への TCP 到達 +GET {mailpitApiUrl}/infoまたは/messagesが 200) を行い、失敗すればblocked: mailpit unreachableとしてこれらのサブフローだけをスキップする。 -
mail 内 token の捕捉:
packages/e2e/src/mailpit.tsのwaitForLatestMessageTo(email)と同じ方式 (Mailpit HTTP API/messagesを受信者アドレスでポーリングし最新メールを取得) で対象メールを取得し、 本文から${baseUrl}/reset-password?token=.../${baseUrl}/activate?token=.../${baseUrl}/confirm-email?token=...のいずれかのパターンに一致するリンクを正規表現で抜き出し、そのリンクへ ナビゲートして charter を進める (extractInviteLinkと同じ「既知の URL パターンへの正規表現マッチ」手法)。捕捉した token / リンクは §12 の redaction 対象に含める (生の値をログ・notes に残さない)。
§5. 認証情報の前提 (自動登録はしない)
crowi-qa は対象 dev DB に既存ユーザー/管理者の認証情報が用意されている
ことを前提にする。環境変数で受け取り、未設定の場合は 該当 charter だけ
blocked: no credentials として次に進む (全体を止めない):
| 環境変数 | 用途 |
|---|---|
CROWI_QA_USER_EMAIL / CROWI_QA_USER_PASSWORD | 一般ユーザー (auth 読み取り専用サブフロー・collab・ページ CRUD 等) |
CROWI_QA_ADMIN_EMAIL / CROWI_QA_ADMIN_PASSWORD | 管理設定 charter (#8、read-only 確認用) |
CROWI_QA_ISOLATED_ADMIN_EMAIL / CROWI_QA_ISOLATED_ADMIN_PASSWORD | isolated DB (isolateDb: true worktree) 限定の mutating サブフロー用。この worktree でまだ資格情報が無ければ人間が一度だけ installer/register を回して用意する |
多くの worktree は既定で main と DB を共有しており (readDevLocalConfig が
返す isolateDb は既定 false)、テストアカウントの自動作成は共有 dev
データを汚すリスクがある — したがって どのケースでも自動登録はしない。
§6. run id・ページ作成 path prefix・cleanup
6.1 run id
<UTC yyyymmdd-HHMMSS>-<4 桁乱数 または pid>-<target> 形式 (例
20260705-134502-7931-main)。同一 target に対して同じ秒に複数の
crowi-qa プロセスが起動しても衝突しないようにする。証跡ルート
(.reviews/qa/<run-id>/) もこの run id を使う。
RUN_ID="$(date -u +%Y%m%d-%H%M%S)-$$-<target>"
6.2 ページ作成の path prefix
charter が作成するページ・コメント・添付は、必ず run 専用の一意な path
prefix 配下に置く: /qa/<run-id>/<charter>/...。
6.3 manifest と hard delete による cleanup
charter は作成したページ・コメントの id を、作成した その場で run の
エビデンスルート配下の manifest ファイル .reviews/qa/<run-id>/created.json
に追記する。配列の各要素は判別用の type フィールドを持つ: ページは
{ type: "page", pageId, path, charter }、コメントは
{ type: "comment", commentId, pageId, charter } (§6.4 の api-fixture
セットアップが seed-fixtures.mjs 経由で書き込む形)。type フィールドの
無い要素 (旧 run の manifest、または browser-editor セットアップが書く従来
形式) は読み取り時に type: "page" として扱う後方互換パーサを使う。
charter 終了時のクリーンアップは、この manifest に記録された type: "page" の page id のみを対象に hard delete (DELETE /pages を
completely: true で呼び、Page.completelyDeletePage に落とす) を行う。
completely: true はレビジョンチェックをバイパスし、bookmark・comment・
attachment (バッキング storage オブジェクトごと)・redirect origin・
activity を丸ごと削除するため、対象を誤ると共有 dev DB の既存データを永久に
失う。api-fixture セットアップ (§6.4) が作成したページ・コメントは QA
専有インスタンス上に存在し、ambient dev 向けのこの hard delete ループの
対象にはしない — それらは同じ run の DB drop (§6.4) でまとめて破棄される。
推奨実装 (実測済み): 認証済みブラウザセッションの
fetch()をevaluate_script経由で manifest の id ごとにループさせる — セッションの 認証がそのまま乗るので curl 用の JWT 取得が不要で、削除の 200/404 応答も その場で確認できる。
- fallback 確認: manifest が壊れている / 読めない場合に 限り、
path が
/qa/<run-id>/...prefix 配下 かつ creator がこの run の QA アカウントと一致するページを候補として列挙し、削除前にfindings.mdに 列挙して manifest 破損の事実とともに報告する (manifest が健全なときは この fallback 列挙は行わない)。この列挙はtype: "page"相当 (typeフィールドの無い旧形式を含む) のみを対象にし、type: "comment"は列挙 対象に含めない (コメントはページではなく、対応するページの hard delete に連鎖して消える)。どちらの経路でも一致しないページには削除も soft-delete もせず、既存データに一切触れない。 - 通常の soft delete は使わない理由:
Page.deletePage(completely省略) はstatus: deletedにして/trash/...へリネーム + redirect page を作るだけで実データ (ページ本体・コメント・添付) は残り、かつPage.pathは unique index なので同一パスを次回 run が再利用すると 衝突する。したがって manifest (または fallback で確認できた) ページは hard delete で完全に除去し、それ以外のページはゴミ箱にも入れず何もしない。 - hard delete に失敗した場合は
summary.mdに残留物 (page id / path) を 明記する (運用側が手動で削除するための情報)。
6.4 api-fixture セットアップと QA 専有インスタンス
table・backlink・検索結果・コメント・grant 可視性のように、検証対象が
「保存済みページの見え方」であるチャーターは、エディタでページ本文を手打ち
する代わりに .claude/skills/crowi-qa/scripts/seed-fixtures.mjs (Node 標準
fetch のみに依存 — Mongo driver・workspace パッケージの import には依存
しない) でページ/コメントをシードし、作成した URL へ直接ナビゲートして表示
確認から始める。エディタ状態そのものが検証対象のチャーター (#2・#4) は
このセットアップを使わず、従来どおり ambient dev に対するブラウザ/エディタ
操作でセットアップする。
6.4.1 setup mode ルーティング表
| charter | setup mode | 専有インスタンス起動不可時 | 理由 |
|---|---|---|---|
| collab / presence (#2) | browser-editor (ambient dev) | — (専有インスタンスを使わない) | Yjs/エディタ状態と WS 挙動そのものが検証対象 |
| エディタ保存・draft (#4) | browser-editor (ambient dev) | — (同上) | エディタ操作そのものが検証対象 |
| ページ CRUD・rename・trash (#3) のうち table/markdown 表示確認 | api-fixture (専有インスタンス) | browser-editor へフォールバック | 保存済みページの表示確認であり入力操作は不要 |
| backlink 表示 | api-fixture (target→source の順序制約 + readiness polling 必須。6.4.4 参照) | browser-editor へフォールバック | save イベント由来の backlink 副作用を検証したい |
| grant 可視性 (シード本人が owner のページの表示確認) | api-fixture | browser-editor へフォールバック | 単一 identity で作成・閲覧できる範囲に限定 (owner 視点のみ) |
| grant access-denied (別ユーザーからの拒否確認) | browser (api-fixture 化しない) | 変更なし | 第二 identity が必要で今回のスコープ外 |
| 検索結果 (新規保存したページがヒットするかの確認) | api-fixture (503 short-circuit + readiness polling 必須。6.4.4 参照) | browser-editor へフォールバック | 実際のページ保存と検索側副作用が必要。per-run DB は search 未設定が既定なので 503 → 即 blocked が通常パス |
| 検索の grant フィルタリング (非 owner に非表示になることの確認) | browser (api-fixture 化しない) | 変更なし | 第二 identity が必要で今回のスコープ外 |
| コメント (#5) | api-fixture (同一 run が作った fixture ページに対してのみ) | browser-editor へフォールバック | コメント作成はセットアップ、UI 表示が検証対象 |
| 管理設定 (#8) | 既定 browser または read-only (§7) | 変更なし | ambient 共有 DB での config mutation は §7 で禁止 |
| 添付・アップロード (#9) | アップロード操作自体の検証は browser。既存ファイルの表示確認のみなら対応する API があれば api-fixture (対応する attachment API が今回未検証のため、現状は browser のまま) | 変更なし | アップロード操作自体が検証対象になり得る |
認証チャーター (#1) は既存の §4.1/§4.2 の 2 層構成をそのまま維持し、この 表の対象に含めない (すでに read-only/isolated-mutating の分離ルールが あるため)。
6.4.2 QA 専有インスタンス: 書き込み先を「判定」せず「所有」する
api-fixture セットアップのチャーターが 1 つでも run に含まれる場合、run
は最初に一度だけ QA 専有インスタンスを起動する。mutating なフィクスチャ
書き込みは常にこの専有インスタンスに対してのみ行い、ambient dev インスタ
ンス (共有 DB・isolated DB を問わず) には一切 mutating な書き込みをしない。
書き込み先の安全性は「環境の分類・判定」ではなく「自分で名前を決めて自分で
起動した」というコンストラクションで保証する — §14.3-§14.7 の
--prod-build 用インスタンスと同じ発想の dev 版であり、機構は流用する
(新設しない)。
-
起動は run につき最大 1 回: 複数の
api-fixtureチャーターが専有 インスタンス 1 本を共有する。api-fixtureチャーターが 1 つも無い run では起動しない (既存挙動に回帰なし)。 -
--prod-buildとの関係:--prod-buildrun では §14 のインスタンス がそのまま seeding 対象になる。専有インスタンスを二重起動しない。 -
DB 名:
crowi_qa_dev_<run-id>(§14.3 のcrowi_qa_prod_<run-id>と 同じ命名スタイル。run id は §6.1 の既存 run id)。MongoDB は最初の書き込み で DB を暗黙に作成するため、作成手順は不要。63 バイト制限に触れる場合は run id 部分を短縮ハッシュにする。 -
env サニタイズ: §14.3 と同一の理由・同一の手順。
MONGOLAB_URI/MONGODB_URI/MONGOHQ_URLを明示的に unset した上でMONGO_URIだけ を設定して起動する。dev モードの api はapps/crowi-runnerを cwd にtsxで起動する (packages/api/package.jsonのdevスクリプトと同じ cwd 解決。tsx watchの watch は不要 — 専有インスタンスは 1 回起動して run 終了時に落とすだけなので、素のtsxでよい):cd apps/crowi-runner env -u MONGOLAB_URI -u MONGODB_URI -u MONGOHQ_URL \ MONGO_URI="mongodb://localhost/crowi_qa_dev_<run-id>" \ NODE_ENV=development PORT=<apiPort> \ npx tsx --tsconfig ../../packages/api/tsconfig.json \ --env-file-if-exists=../../.env \ ../../packages/api/src/app.ts--env-file-if-existsで読む repo-root.envにMONGO_URIが定義され ていても、Node の--env-fileはプロセスに 既に設定済みの 環境変数を ファイルの値で上書きしない (Node の仕様) ため、上記の明示MONGO_URIが 優先される。web はpackages/webを cwd にnext devを起動し、PORT_WEB=<webPort>/CROWI_API_URL=http://localhost:<apiPort>を渡す (scripts/dev.mjsがpnpm devに注入するのと同じ変数)。前段の同一 オリジン proxy はscripts/dev-caddy.mjsのstartNodeProxyFallback()を流用する (§14.5 と同じ — dev-port registry には登録しない使い捨て ポートを OS プローブで都度選ぶ)。unset が信頼できない場合は §14.3 と同じ く fail-closed でblocked: conflicting Mongo env var present — cannot guarantee isolationとして起動しない。起動後は接続先 DB 名をログで確認 してから provisioning に進む。 -
provisioning: §14.4 と同一手順の dev 版。
GET <一時 proxy>/api/installerでinstaller_requiredを確認し、POST /installer/createAdminを run 専用の固定資格情報 (実運用の秘密情報では ない固定値。例crowi-qa-fixture@example.com/ 固定パスワード —packages/e2e/src/config.tsのe2eUsers.adminと同じ発想) で呼ぶ。already_installedが返る想定外のケースは同じ資格情報でログインを試し、 失敗すればblocked: qa DB in unexpected stateで打ち切る。 -
後始末: §14.7 と同一機構。成否によらず api/web/proxy プロセスを終了 し、
crowi_qa_dev_<run-id>を drop する。drop 失敗時はsummary.mdに 残留 DB 名を明記する。 -
フォールバック: 専有インスタンスの起動に失敗した場合 (ポート確保 不可・Mongo 不達等) は
blocked: qa-owned instance failed to start (<reason>)とし、該当api-fixtureチャーターは従来の browser-editor セットアップ (ambient dev 上でエディタ手打ち) へフォールバックする — 検証能力自体は後退しない。ambient dev への mutating write はこの フォールバックでも従来の境界 (§4.2・§5・§7) に従う。
6.4.3 seed-fixtures.mjs の使い方
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 1k
- Forks
- 165
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
crowi-qa- Source
- github.com/crowi/crowi