Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
81605bc
Preserve View tab order during migration
SIkebe Aug 18, 2026
69e178a
Cover View tab reorder failure paths
SIkebe Aug 18, 2026
7beee8f
Paginate View order reads
SIkebe Aug 18, 2026
b90ae12
Merge remote-tracking branch 'origin/main' into sikebe-preserve-view-…
SIkebe Aug 18, 2026
00b0273
Exercise View order through the live API
SIkebe Aug 18, 2026
4c55ca7
Distinguish duplicate Views when verifying order
SIkebe Aug 18, 2026
a9979e5
Use UI settings for duplicate View order
SIkebe Aug 18, 2026
93c45cc
Expand credentialed API migration coverage
SIkebe Aug 18, 2026
39ca576
Use the Integration project in manual commands
SIkebe Aug 18, 2026
e3e33dc
Reuse the fixture PR in live relink coverage
SIkebe Aug 18, 2026
ed4fafe
Keep test-agent artifacts out of the repository
SIkebe Aug 18, 2026
0389689
Wait for complete View order reads
SIkebe Aug 18, 2026
8e3ae69
Wait for browser View order visibility
SIkebe Aug 18, 2026
7a6f6fe
Align E2E policy settings with validation
SIkebe Aug 19, 2026
449d3da
Derive browser account topology from E2E settings
SIkebe Aug 19, 2026
d2fe46e
Use DOM order for saved Project tabs
SIkebe Aug 19, 2026
cda6767
Parse relative View tab links consistently
SIkebe Aug 19, 2026
05cdec7
Scope API-only View assertions to tab order
SIkebe Aug 19, 2026
f207cfb
Keep tab-order UI failures recoverable
SIkebe Aug 19, 2026
f15564f
Name View and PR tests for actual coverage
SIkebe Aug 19, 2026
898c0e7
Remove the obsolete GraphQL View importer overload
SIkebe Aug 19, 2026
2942ffb
Detect when GraphQL View order becomes reliable
SIkebe Aug 19, 2026
ef9a070
Limit the GraphQL order canary to known divergent fixtures
SIkebe Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/skills/ghpmv-e2e-validation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ agent が terminal に command を直接入力できず、ユーザー自身が

Step 1の質問を始める前に、`GHPMV_E2E_SETTINGS`が設定されている場合は、そのpathだけをauthoritativeなJSONCとして読む。明示pathが存在しない、読み取れない、またはvalidationに失敗した場合は、local/shared fileへfallbackせず、pathとエラーを示して修正されるまで停止する。`GHPMV_E2E_SETTINGS`が未設定の場合だけ、`tests/e2e.settings.local.jsonc`、`tests/e2e.settings.jsonc`の順で最初に存在するfileを読む。`//`コメントと末尾commaを許可する。設定値は次の用途に使い、同じ非secret値を再質問しない。

自動検出ではglobやtracked-file一覧だけに依存しない。`tests/e2e.settings.local.jsonc`はgitignore対象のため、file search結果に現れないことがある。必ずrepository rootから`Test-Path -LiteralPath tests/e2e.settings.local.jsonc`でexact pathの存在を先に確認し、存在すればshared fileを読む前にlocal fileを読む。local fileが存在するのにshared fileを先に読んだり、localに非空で設定されたlogin、organization、repository、policy confirmationを再質問したりしてはならない。

- source / target Organization、API / Web / uploads URL、browser profile
- Integration / Browser fixtureのProject番号とsource / target repository
- source / target browser login、collaborator login、EMUを含むuser mapping
Expand All @@ -194,6 +196,8 @@ Step 1の質問を始める前に、`GHPMV_E2E_SETTINGS`が設定されている

自動検出したlocal/shared fileでは、空文字、存在しないlocal resource、現在のhostと矛盾するURL、またはschema validationに失敗する値を確定値として扱わず、その項目だけを通常どおり質問する。明示指定した`GHPMV_E2E_SETTINGS`のエラーだけはfallbackや質問による補完をせず停止する。JSONCにはPAT値、cookie、browser storage-state内容を保存させない。`tokenEnvironmentVariable`などの値は環境変数名であり、secretそのものではない。

`browser-e2e`で`users.sourceBrowserLogin`と`users.targetBrowserLogin`が両方とも非空なら、source / target browser accountが同一か別かを質問しない。account identityは正規化した`webBaseUrl` hostとbrowser loginの組で機械判定する。同じhostでloginがcase-insensitiveに一致する場合だけ同一account、hostまたはloginが異なる場合は別accountとして記録する。どちらかのloginが空の場合だけ不足しているloginを一件ずつ質問し、両方確定後に同じ規則で判定する。

`source.apiBaseUrl` / `target.apiBaseUrl`はghpmv用GraphQL endpointで、`https://api.TENANT.ghe.com/graphql`またはtenant API originのどちらも受け付ける。GEIの`--github-source-api-url` / `--target-api-url`へ渡す値は別に導出し、末尾のoptional `/graphql`とtrailing slashを除いた`https://api.TENANT.ghe.com` originを使う。GraphQL endpointをそのままGEI argumentへ再利用しない。

settings由来のOrganization loginは`^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$`、repository short nameは`^(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$`、user loginは`^[A-Za-z0-9](?:[A-Za-z0-9_-]{0,98}[A-Za-z0-9])?$`で検証する。これらを含むsettings由来の文字列をPowerShell commandへ渡す場合は、profileだけでなくOrganization、repository、login、URL、title、path、mapping値をすべてsingle-quoted argumentにする。値のpatternがsingle quoteを許可する項目では、既存の規則どおり`'`を`''`へ置換してから囲む。検証済みであってもunquoted substitutionは行わない。
Expand Down Expand Up @@ -251,7 +255,7 @@ GEI roleは`GHPMV_GEI_SOURCE_TOKEN` / `GHPMV_GEI_TARGET_TOKEN`にだけ適用す
1. source host type: **GitHub.com(通常の GHEC を含む)** または **GHEC with data residency (`*.ghe.com`)**
2. `api-only` / `browser-e2e` では target host type も同じ二択で確認する。
3. data residency を選んだ側ごとに、placeholder ではない tenant web URL (`https://TENANT.ghe.com`) を自由入力の質問カードで確認する。対応する API URL (`https://api.TENANT.ghe.com`) を導出して別の確認カードで提示し、確定する。target側ではuploads URL (`https://uploads.TENANT.ghe.com`) も導出して別の確認カードで確定する。
4. `browser-e2e` では source / target の browser account が同一か別かを host とは別の質問で確認する
4. `browser-e2e` では、settingsのbrowser loginが片側でも空の場合だけ不足loginを確認する。両login確定後は`(web host, login)`で同一/別accountを機械判定し、同一か別かを別質問で確認しない

GitHub.com は web URL `https://github.com`、API URL `https://api.github.com/graphql` として記録する。特に **GitHub.com source → GHEC with data residency target** を `github.com-to-ghec-dr` として一級シナリオにする。この topology では source command は既定の GitHub.com endpoint を使い、target command と target browser profile だけに tenant endpoint を指定する。host が異なる場合は login 文字列が似ていても `source` / `target` browser profile と token を必ず分ける。

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
# Local manual migration test artifacts
.manual-test/

# Local test-generation agent artifacts
.testagent/

# Local E2E settings may contain organization, repository, and user names.
tests/e2e.settings.local.jsonc

Expand Down
23 changes: 13 additions & 10 deletions docs/BROWSER_AUTOMATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ GraphQL と Playwright を組み合わせた View・Workflow 移行の詳細設
| **Slice by** | ❌ API に無い → **UI で読む** | **UI** | |
| **Field sum** | ❌ API に無い → **UI で読む** | **UI** | |
| **Roadmap 設定(Dates / Zoom / Markers)** | ❌ API に無い → **UI で読む** | **UI** | |
| タブの並び順 | GraphQL `views`(orderBy: POSITION) | **UI**(タブの drag & drop)| v1 では省略可(§8) |
| タブの並び順 | **UI**(`navigation "Select view"` 内のsaved tab `href`順) | **UI**(タブの drag & drop) | GraphQL `POSITION`は現行UIのsaved-tab順と乖離する場合がある。`ViewSnapshot.tabPosition`はschema v1のnullable additive field |

### Workflow のプロパティ別ソースマップ

Expand Down Expand Up @@ -143,11 +143,11 @@ internal static class Sel
| V-7 | Roadmap | Dates(date フィールド対 or iteration)、Zoom(Month/Quarter/Year)、Markers |
| V-8 | 全レイアウト共通 | filter 文字列(そのまま転記。フィールド名は移行済み前提で互換) |
| V-9 | 全レイアウト共通 | Slice by |
| V-10 | View の name / タブ並び順 | 並び順は v1 スコープ外(§8) |
| V-10 | View の name / タブ並び順 | browser-assisted export/verifyでDOM `href`順を読み、browser-assisted importで最小D&Dを適用 |

### 3.2 export: UI からの読み取り手順(API に無い 3 項目のみ)
### 3.2 export: UI からの読み取り手順(APIで正しく取得できない4項目)

対象: Slice by / Field sum / Roadmap 設定。GraphQL export の後、view ごとに 1 回だけページを開いて補完する
対象: saved-tab order / Slice by / Field sum / Roadmap設定。GraphQL exportの後、viewごとに1回だけページを開いて補完し、最後にtab stripのDOM順を取得する

```
手順(view ごと):
Expand All @@ -158,6 +158,7 @@ internal static class Sel
- "Field sum: <fields>" → `ViewUiSnapshot.FieldSum`
- Roadmap のみ: "Dates: <...>", "Zoom level: <Month|Quarter|Year>", "Markers: <...>"
4. Esc でメニューを閉じる
5. `navigation "Select view"`内のsaved tab `href`をDOM順に列挙し、View numberへ変換して`tabPosition`を付与する
```

実装メモ: メニュー項目は「設定名 + 現在値」を accessible name に含むため、label prefix で特定する。複数選択項目は overlay の `aria-checked` を読む。
Expand All @@ -176,12 +177,13 @@ EnrichView(spec, targetViewNumber):
6. Field sum: ViewOptions → "Field sum" → spec.FieldSum の各フィールドをチェック
7. Roadmap のみ: "Dates" → 開始/終了フィールド対 or iteration を選択、"Zoom level"、"Markers" のチェック群
8. 保存: View menu → "Save view" → alertdialog の "Save"(dialog が出ない UI variant では直接保存)
9. 検証は後続の `ghpmv verify --enable-browser-automation` または browser E2E で行う
9. 全 View 設定の適用後、target のDOM `href`順と snapshot順から最小移動計画を作り、必要なタブだけdrag-and-drop
10. 検証は後続の `ghpmv verify --enable-browser-automation` または browser E2E で行う
```

Project conflict は API View stage より前に `--on-conflict skip|update|fail` または `--project-number` で解決する。API importer は source view number と target view number の対応を返し、browser importer はその View に未公開設定だけを適用する。

作成順序: **スナップショットの view number 昇順**で作成(タブ順が概ね再現される)。デフォルトで作られる "View 1" は、スナップショット先頭の view で上書き(rename + 設定)して消費する。
作成順序: browserで取得した`tabPosition`があるsnapshotはその昇順、未取得のsnapshotはview number昇順で作成する。デフォルトで作られる"View 1"はsnapshotの先頭Viewで上書き(rename + 設定)して消費する。API-only importはtab orderを適用できない旨をwarningにし、browser-assisted importはView設定適用後にtargetのDOM順を読み取って修復する

---

Expand Down Expand Up @@ -251,7 +253,7 @@ ApplyWorkflow(spec):
```jsonc
{
"views": [{
"number": 1, "name": "Backlog", "layout": "TABLE_LAYOUT",
"number": 1, "tabPosition": 0, "name": "Backlog", "layout": "TABLE_LAYOUT",
"filter": "is:issue -status:Done",
"visibleFields": ["Title", "Assignees", "Status", "Priority"], // 列順そのまま
"groupBy": ["Status"], "verticalGroupBy": [],
Expand Down Expand Up @@ -282,8 +284,8 @@ ApplyWorkflow(spec):

browser importer 自体は各 view / workflow の適用直後に完全な read-back diff を行わない。移行後は `ghpmv verify --enable-browser-automation` が次を比較する:

1. **API で読める項目**: GraphQL で対象 view を `views(first:50)` から number 一致で取得し、`layout / filter / groupByFields / sortByFields / verticalGroupByFields / fields(POSITION順)` を spec と比較
2. **UI でしか読めない項目**: §3.2 / §4.2 の export 用読み取りルーチンを**そのまま再利用**してターゲットを再スクレイプし、spec.ui と比較
1. **API で読める項目**: GraphQLで対象viewをnumber一致で取得し、`layout / filter / groupByFields / sortByFields / verticalGroupByFields / visibleFields`をspecと比較
2. **UI でしか読めない項目**: saved-tab DOM順と§3.2 / §4.2のexport用読み取りルーチンを**そのまま再利用**してターゲットを再スクレイプし、`tabPosition`と`spec.ui`を比較
3. 差分は `verify` コマンドと同じレポーター(期待値/実測値/対象)で出力

手動実行する `BrowserRoundTripTests` は View と Workflow のラウンドトリップを別々のテストに分け、それぞれ
Expand Down Expand Up @@ -312,7 +314,7 @@ v1 対象外項目の将来対応方針(v1.x / v2)は [PLAN.md §8「スコー
| 項目 | 判断 |
|---|---|
| 表示フィールドの列順 | GraphQL `visibleFields` / `visibleFieldIds` で明示的に再現する |
| View タブの並び順(D&D のみ) | v1 スコープ外。import 後に警告で「手動で並び替えてください」と案内 |
| View タブの並び順(D&D のみ) | 対応済み。LIS を残す最小 D&D 計画を使い、overflow tab は `ScrollIntoViewIfNeededAsync` 後に操作。既に一致する場合は drag しない |
| disabled workflow への設定適用 | 対応済み。設定保存後に toggle off へ戻す |
| memex 内部 API の直接利用 | 既定では不採用。HAR は現時点で成果物として記録していない。UI 操作不能項目が出た場合に調査・取得を検討 |
| UI 変更による破損 | リリース前の手動 browser E2E と `docs/ui-maps/` の実測記録で確認。回復可能な破損は warning + 対象設定の skip。scheduled/nightly CI は未実装 |
Expand All @@ -331,3 +333,4 @@ v1 対象外項目の将来対応方針(v1.x / v2)は [PLAN.md §8「スコー
| B7 | Workflow import(§4.3)W-1〜W-8 | 完了 |
| B8 | Workflow import W-9(Auto-add 複数 + 上限処理) | 完了。実装上限は 20 |
| B9 | ラウンドトリップ E2E(§6) | テスト実装済み・手動実行。scheduled/nightly CI は未実装 |
| B10 | View tab DOM-order export / verify + browser D&D import | 完了。API-only / 旧snapshotのnullは比較・修復対象外 |
Loading