diff --git a/.agent/plans/2026-02-07-manager-log-refactor.md b/.agent/plans/2026-02-07-manager-log-refactor.md deleted file mode 100644 index 8f72dae62..000000000 --- a/.agent/plans/2026-02-07-manager-log-refactor.md +++ /dev/null @@ -1,101 +0,0 @@ -# ExecPlan: 管理操作ログ機構の改修(監査機能の強化) - -## Status: 方針検討中 - -このプランは未着手です。システムログ機構の改修(`2026-02-07-logging-system-refactor.md`)完了後に方針を決定します。 - -## Purpose / Big Picture -Evolution CMS の管理操作ログ(管理者の操作履歴)を改修し、監査機能を強化する。誰がいつ何を変更したかを追跡可能にし、コンプライアンス要件に対応する。 - -**対象範囲**: 管理操作ログ(`manager_log` テーブル)のみ。 - -## Current State - -### 既存実装 -- **テーブル**: `manager_log` (timestamp, internalKey, username, action, itemid, itemname, message) -- **記録クラス**: `logHandler` (`manager/includes/log.class.inc.php`) -- **管理画面**: `manager/actions/report/logging.static.php` (a=13) -- **影響範囲**: 28箇所 - -### 主要ユースケース -- 管理者が特定のドキュメントをいつ編集したか -- 特定期間にどのユーザーがログインしたか -- ユーザー削除などの重要な操作履歴 -- 統計レポート(月次・年次の操作傾向) - -### 現在の問題点 -- DB肥大化(バックアップ失敗の一因) -- 構造化されていない(`message` カラムは平文) -- ローテーション機能が非効率(TRIM方式) -- 長期保存の仕組みがない - -## Design Options - -### オプションA: ファイルベース移行(システムログと統一) -**メリット**: -- システムログとの一貫性 -- DB肥大化の解決 -- 長期アーカイブが容易 - -**デメリット**: -- 検索性の低下(期間・ユーザーによる絞り込みが遅い) -- 統計レポートが困難 -- 既存の管理画面を全面刷新が必要 - -**実装方針**: -- `temp/logs/manager/YYYY/MM/manager-YYYY-MM-DD.log` (JSONLines) -- CLIコマンド: `log:search manager --user=5 --action=edit` - -### オプションB: DB保存継続 + 構造化 -**メリット**: -- 高速な検索(WHERE句による絞り込み) -- 統計レポートが容易(GROUP BY, COUNT等) -- 既存の管理画面を流用可能 - -**デメリット**: -- DB肥大化問題は別途対処が必要 -- 長期保存の仕組みを別途実装 - -**実装方針**: -- `message` カラムを廃止、構造化カラム追加(`context` JSON型) -- パーティショニング導入(月次テーブル分割) -- 古いデータの自動アーカイブ(CSV/JSONファイル出力) - -### オプションC: ハイブリッド(短期: DB、長期: ファイルアーカイブ) -**メリット**: -- 直近データは高速検索(DB) -- 古いデータはコスト削減(ファイル) -- 両方のメリットを活用 - -**デメリット**: -- 実装が複雑 -- 検索時に両方を見る必要がある - -**実装方針**: -- 直近90日: `manager_log` テーブル(DB) -- 91日以降: `temp/logs/manager/archive/YYYY-MM.jsonl` -- 定期的なアーカイブジョブ(CLI: `log:archive manager`) - -## Questions to Answer - -1. **保存期間の要件**: 最低何日間保存する必要があるか?(法令対応) -2. **検索頻度**: 管理操作ログを検索する頻度は?(日次・週次・月次) -3. **統計レポート**: 操作傾向の統計は必要か? -4. **パフォーマンス**: 現在のDB検索に問題があるか? -5. **改ざん防止**: 監査ログとして改ざん防止機能は必要か? - -## Decision Log -(方針決定後に記載) - -## Next Steps - -1. システムログ機構の改修を完了 -2. 現在の管理操作ログのユースケース・要件を整理 -3. 3つのオプションを評価 -4. 方針決定後、このプランを更新して実装開始 - -## References -- [システムログ改修プラン](.agent/plans/2026-02-07-logging-system-refactor.md) -- [ロードマップ](assets/docs/roadmap.md) -- 現在の実装: `manager/includes/log.class.inc.php` -- 現在の管理画面: `manager/actions/report/logging.static.php` diff --git a/.agent/plans/2026-02-21-evo-cli-user-management.md b/.agent/plans/2026-02-21-evo-cli-user-management.md new file mode 100644 index 000000000..c0d3cd06b --- /dev/null +++ b/.agent/plans/2026-02-21-evo-cli-user-management.md @@ -0,0 +1,225 @@ +# ExecPlan: EVO CLI ユーザー管理コマンド追加 + +## Purpose / Big Picture + +開発時に管理画面へ入れない状況でも、CLIだけで管理ユーザーの CRUD(作成・参照・更新・削除)を実行できるようにする。AIの自動実行(非対話)と人間の手動実行(対話)の両方に対応し、直接SQL更新の運用リスクを下げる。 + +## Progress + +- [ ] (2026-02-21) CLIの既存構造とユーザー認証ハッシュ仕様を確認する +- [ ] (2026-02-21) `user:create` / `user:update` の対話/非対話インターフェースを確定する +- [ ] (2026-02-21) `user:list` / `user:show` コマンドを実装する +- [ ] (2026-02-21) `user:create` コマンドを実装する +- [ ] (2026-02-21) `user:update` コマンドを実装する +- [ ] (2026-02-21) `user:set-password` コマンドを実装する +- [ ] (2026-02-21) `user:unlock` コマンドを実装する +- [ ] (2026-02-21) `user:delete` コマンドを実装する +- [ ] (2026-02-21) READMEと受け入れ確認手順を更新する + +## Surprises & Discoveries + +- 現行CLIには `user:*` コマンドがなく、ユーザー管理は `db:query` で直接更新するしかない。 +- 既存CLIは `--yes` や環境変数確認のみで、対話入力の共通仕組みをまだ持っていない。 +- ログイン処理は `phpass` / `md5` / `v1` を受け入れるが、`md5` ログイン成功時は自動で `phpass` に再ハッシュされる。 +- ロックアウト判定は `modx_user_attributes` の `failedlogincount` / `blocked` / `blockeduntil` に依存する。 +- 既存の管理画面削除処理は `manager_users` だけでなく `member_groups` / `user_settings` / `user_attributes` も同時削除している。 + +## Decision Log + +- 2026-02-21 / AI / コマンド群は CRUD を基軸に `user:list`、`user:show`、`user:create`、`user:update`、`user:delete` を中核とし、運用頻度の高い更新系として `user:set-password` と `user:unlock` を併設する。根拠: 学習コストを下げつつ、復旧と通常運用の両方をCLIで完結するため。 +- 2026-02-21 / AI / `user:create` は TTY 接続時に対話モード、`--no-interaction` または引数完備時は非対話モードで動作させる。根拠: 人間利用の操作性とAI利用の再現性を同時に満たすため。 +- 2026-02-21 / AI / `user:update` も `user:create` と同一の対話規約(TTY時の質問、`--no-interaction` 時は問い合わせ禁止)にそろえる。根拠: コマンド間で操作体験と自動化規約を統一するため。 +- 2026-02-21 / AI / 対話モードで収集する入力は `username`、`password`(確認入力あり)、`email` を必須とし、`fullname` は任意にする。根拠: 管理ユーザー作成に必要十分で、入力負担を増やしすぎないため。 +- 2026-02-21 / AI / パスワードは平文引数を推奨せず、非対話時は `--password-stdin` を優先提供する。根拠: 履歴・プロセス一覧への露出を下げるため。 +- 2026-02-21 / AI / `user:set-password` のデフォルトは `phpass` を使用し、`--hash=md5` は開発用途オプションとして明示的指定時のみ許可する。根拠: 既存認証仕様と安全性の両立。 +- 2026-02-21 / AI / 対象ユーザー指定は `--username=` 優先、未指定時は第1引数を後方互換として許容する。根拠: 既存CLI利用者の入力ミスを減らす。 +- 2026-02-21 / AI / `user:delete` は既存管理画面の削除整合性に合わせて関連テーブル(`member_groups`、`user_settings`、`user_attributes`)も削除し、`--yes` または対話確認を必須化する。根拠: 孤児データ防止と誤操作防止の両立。 +- 2026-02-21 / AI / 出力はレスポンススキーマをJSONに統一しつつ、既定の表示モードは `--format=auto` とし TTYでは `text`、非TTYでは `json` を選ぶ。根拠: 人間の可読性とAI自動処理の両立。 +- 2026-02-21 / AI / `user:create` / `user:update` / `user:delete` は `--dry-run` を持ち、実変更なしで検証できるようにする。根拠: AI実行時の安全性と事前検証性を上げるため。 +- 2026-02-21 / AI / 複数テーブル更新を伴う処理はDBトランザクションで一括制御し、途中失敗時はロールバックする。根拠: 部分成功による不整合を防ぐため。 + +## Outcomes & Retrospective + +実装完了後に記載。 + +## Context and Orientation + +用語: + +- `TTY`: 端末に直接接続された入出力。対話プロンプトを安全に出せる実行環境。 +- `非対話モード`: 引数と標準入力だけで完了する実行形態。AIやCI実行で使う。 +- `phpass`: 現行の推奨パスワードハッシュ方式。`evo()->phpass->HashPassword()` で生成する。 +- `manager_users`: 管理ユーザー本体テーブル。`id`、`username`、`password` を保持する。 +- `user_attributes`: ログイン失敗回数やロック状態を保持する属性テーブル。 +- `member_groups`: 管理ユーザーと権限グループの関連を保持する中間テーブル。 +- `user_settings`: 管理ユーザーの個別設定を保持するテーブル。 + +対象ファイル: + +- `manager/includes/cli/commands/user-list.php`(新規) +- `manager/includes/cli/commands/user-show.php`(新規) +- `manager/includes/cli/commands/user-create.php`(新規) +- `manager/includes/cli/commands/user-update.php`(新規) +- `manager/includes/cli/commands/user-set-password.php`(新規) +- `manager/includes/cli/commands/user-unlock.php`(新規) +- `manager/includes/cli/commands/user-delete.php`(新規) +- `manager/includes/cli/README.md` +- (必要時)`manager/includes/cli/cli-helpers.php` + +関連既存コード: + +- `evo`(コマンド解決エントリポイント) +- `manager/processors/login.processor.functions.php`(認証ハッシュの挙動) +- `manager/includes/cli/commands/db-query.php`(CLI出力/エラー処理パターン) +- `manager/includes/cli/commands/log-clear.php`(明示確認フラグの運用パターン) +- `manager/processors/permission/delete_user.processor.php`(削除時の関連データ整合性) + +## Plan of Work + +先に CRUD の責務をコマンド単位で固定する。`user:create` / `user:update` は人間向け質問フローとAI向け非対話を同じ保存層へ集約し、`user:list` / `user:show` は参照系としてレスポンススキーマを統一する。`user:delete` は削除関連テーブルの整合を保証する破壊的操作として、確認プロセスを必須化する。具体的には「入力収集層(対話/非対話)」「正規化・検証層」「保存/削除層(DB書き込み)」に責務を分離し、SSOTとして保存/削除ロジックは1箇所に統一する。`user:set-password` と `user:unlock` は `user:update` を置き換えるものではなく、運用上頻出する更新特化コマンドとして併設する。出力は `--format=auto` を既定にし、TTYでは人間向け `text`、非TTYでは機械向け `json` を返す。更新系は `--dry-run` とトランザクションで安全性を担保する。 + +対話フローの基本順序は以下とする。 + +1. `username` 入力(未入力・既存重複は再入力) +2. `password` 入力(表示しない) +3. `password_confirm` 入力(一致しない場合は再入力) +4. `email` 入力(形式不正は再入力) +5. `fullname` 入力(空許可) +6. 確認表示(作成内容サマリー。パスワードはマスク) +7. 実行確認(`[y/N]`) + +`--no-interaction` 時は必須値不足を即エラー終了し、追加の問い合わせをしない。これによりAI実行でも決定論的に扱える。 + +### `user:create` CLIインターフェース仕様(確定案) + +基本形: + +- `php evo user:create [options]` + +オプション一覧: + +- `--username=`: 作成するログインID。未指定時は対話モードで質問。非対話では必須。 +- `--email=`: メールアドレス。未指定時は対話モードで質問。非対話では必須。 +- `--fullname=`: 表示名。未指定時は対話モードで質問(空許可)。非対話では任意。 +- `--role=`: ロールID。既定は `1`(管理者)。存在しないIDはエラー。 +- `--password=`: 平文パスワードを引数で受け取る(互換用途)。`--password-stdin` と同時指定不可。 +- `--password-stdin`: 標準入力1行目をパスワードとして受け取る。非対話での推奨方式。 +- `--hash=`: パスワード保存方式。既定 `phpass`。`md5` は明示指定時のみ許可。 +- `--no-interaction`: 追加質問を禁止し、必須値不足は即エラー。 +- `--yes`: 最終確認をスキップして作成を実行(対話確認を省略)。 +- `--dry-run`: 作成内容を検証して結果だけ表示し、DB更新は行わない。 +- `--format=`: 出力形式。既定 `auto`(TTYは `text`、非TTYは `json`)。 + +入力ソース優先順位: + +1. 明示オプション(`--username` 等) +2. `--password-stdin`(パスワードのみ) +3. 対話入力(TTYかつ `--no-interaction` なし) + +バリデーション規約: + +- `username`: 1文字以上100文字以下、既存ユーザー名と重複不可。 +- `password`: 8文字以上を推奨。空文字は禁止。対話時は確認入力一致が必須。 +- `email`: 既存管理画面と同等の形式チェックを通過すること。 +- `role`: `user_roles.id` に存在すること。 +- 排他制約: `--password` と `--password-stdin` は同時指定不可。 + +終了コード: + +- `0`: 成功(ユーザー作成完了) +- `2`: 使い方エラー(未知オプション、必須引数不足、排他違反) +- `3`: 入力値検証エラー(重複ユーザー名、不正メール、不正ロール、パスワード不一致) +- `4`: 実行拒否(非TTYで対話必須、`--no-interaction` かつ入力不足、確認で中止) +- `5`: DB更新失敗(insert/update失敗、トランザクション失敗) + +出力スキーマ(`json` または `auto` で非TTY時): + +- 成功: `{"ok":true,"code":0,"action":"user:create","data":{"id":,"username":"..."},"meta":{"dry_run":}}` +- 失敗: `{"ok":false,"code":<2|3|4|5>,"error":{"type":"...","message":"...","field":"..."}}` + +## Concrete Steps + +1. 既存仕様を確定し、対話入力の実装方針を決める。 + 編集対象ファイル: なし(調査) + 実行コマンド: `php evo help`、`rg -n "loginPhpass|loginMD5|blockeduntil|failedlogincount" manager/processors/login.processor.functions.php -S`、`rg -n "--yes|EVO_CLI_IMPORT|STDIN|fgets|readline" manager/includes/cli -S` + 期待される観測結果: ハッシュ互換仕様、ロック解除対象カラム、CLI確認フラグの既存パターンが明確になる。 + +2. `user:create` / `user:update` / `user:delete` の入出力契約を定義する。 + 編集対象ファイル: `.agent/plans/2026-02-21-evo-cli-user-management.md`(本計画) + 実行コマンド: `rg -n "user:create|user:update|user:delete|--no-interaction|--password-stdin|--yes" .agent/plans/2026-02-21-evo-cli-user-management.md -S` + 期待される観測結果: 対話/非対話の分岐条件、必須引数、破壊的操作の確認挙動が文書化される。 + +3. `user:list` / `user:show` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-list.php`、`manager/includes/cli/commands/user-show.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-list.php`、`php -l manager/includes/cli/commands/user-show.php`、`php evo user:list --limit=20`、`php evo user:show admin` + 期待される観測結果: `user:list` で複数ユーザーの要約が表示され、`user:show` で `id`、`username`、`email`、`role`、`failedlogincount`、`blocked`、`blockeduntil` を表示できる。 + +4. `user:create` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-create.php`、(必要時)`manager/includes/cli/cli-helpers.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-create.php`、`php evo user:create`、`php evo user:create --no-interaction --username=alice --password-stdin --email=alice@example.com`、`php evo user:create --no-interaction --username=alice --password-stdin --email=alice@example.com --dry-run --format=json` + 期待される観測結果: TTYでは質問フローが開始し、非TTY/`--no-interaction` では不足項目がエラーになる。必須項目が揃うと `manager_users` と `user_attributes` が作成される。`--dry-run` ではDB更新せず検証結果のみ返る。終了コードは仕様表(`0/2/3/4/5`)と一致する。 + +5. `user:update` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-update.php`、(必要時)`manager/includes/cli/cli-helpers.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-update.php`、`php evo user:update --username=alice --email=alice+new@example.com`、`php evo user:update --username=alice` + 期待される観測結果: 非対話では指定項目のみ更新し、対話では変更対象だけ質問して更新できる。 + +6. `user:set-password` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-set-password.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-set-password.php`、`php evo user:set-password --username=admin --password-stdin` + 期待される観測結果: 既定で `phpass` ハッシュへ更新される。`--hash=md5` 指定時は md5値へ更新される。 + +7. `user:unlock` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-unlock.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-unlock.php`、`php evo user:unlock admin` + 期待される観測結果: `failedlogincount=0`、`blocked=0`、`blockeduntil=0` へ更新される。 + +8. `user:delete` を追加する。 + 編集対象ファイル: `manager/includes/cli/commands/user-delete.php` + 実行コマンド: `php -l manager/includes/cli/commands/user-delete.php`、`php evo user:delete alice`、`php evo user:delete alice --yes` + 期待される観測結果: 対話または `--yes` で確認後、`manager_users` / `member_groups` / `user_settings` / `user_attributes` の対象データが削除される。自身の削除は拒否される。更新はトランザクションで実行され、途中失敗時はロールバックされる。 + +9. ドキュメントと統合確認を行う。 + 編集対象ファイル: `manager/includes/cli/README.md` + 実行コマンド: `php evo help`、`rg -n "user:list|user:show|user:create|user:update|user:set-password|user:unlock|user:delete" manager/includes/cli/README.md -S` + 期待される観測結果: `help` にCRUDコマンド群が出現し、READMEに対話/非対話/削除確認の実行例が追加される。 + +## Validation and Acceptance + +1. `php evo help` に `user:list`、`user:show`、`user:create`、`user:update`、`user:set-password`、`user:unlock`、`user:delete` が表示されること。 +2. `php evo user:create` 実行時、`username`→`password`→`password確認`→`email`→`fullname`→最終確認の順で対話入力できること。 +3. 対話入力で不正値(空 `username`、不一致パスワード、不正メール)を与えると、該当項目だけ再入力を求めること。 +4. `php evo user:list --limit=20` で一覧を表示し、`--username-like=adm%` などの条件で絞り込みできること。 +5. `php evo user:create --no-interaction --username=alice --email=alice@example.com` のように必須値不足で実行した場合、問い合わせせずにエラー終了すること。 +6. `printf 'StrongPass!123\n' | php evo user:create --no-interaction --username=alice --password-stdin --email=alice@example.com` で作成成功し、`php evo user:show alice` で確認できること。 +7. `php evo user:create --no-interaction --username=alice --password=aaa --password-stdin --email=alice@example.com` のような排他違反時、終了コード `2` で失敗すること。 +8. `php evo user:create --no-interaction --username=alice --email=bad-email --password=StrongPass!123` で終了コード `3` になること。 +9. `php evo user:create --username=alice` を非TTYで実行し入力不足の場合、終了コード `4` になること。 +10. `php evo user:create --no-interaction --username=alice --password=StrongPass!123 --email=alice@example.com --dry-run --format=json` 実行時、終了コード `0` かつ `meta.dry_run=true` を返し、ユーザーは作成されないこと。 +11. `php evo user:create --no-interaction --username=alice --password=StrongPass!123 --email=alice@example.com` をTTYで実行した場合、既定表示が `text` になること。`--format=json` 指定時のみJSON表示になること。 +12. `php evo user:update --username=alice --email=alice+new@example.com` 実行後、`user:show` の `email` が更新されること。 +13. `php evo user:set-password --username=alice --password-stdin` 実行後、`user:show` 上のハッシュ形式が `phpass` であること。 +14. `php evo user:unlock alice` 実行後、`failedlogincount` / `blocked` / `blockeduntil` が 0 になること。 +15. `php evo user:delete alice` は確認プロンプトを表示し、`php evo user:delete alice --yes` は確認なしで実行されること。 +16. `user:delete` 実行後、対象ユーザーが `manager_users` / `member_groups` / `user_settings` / `user_attributes` から消えること。 + +## Idempotence and Recovery + +`user:list` / `user:show` / `user:unlock` は同じ引数で再実行しても同じ結果へ収束する。`user:create` は `username` 重複時に新規作成せずエラー終了するため、再実行時は入力値修正か `user:update` へ切り替える。`user:update` と `user:set-password` は復旧用に再実行可能とし、誤更新時は同コマンドで再設定できる。`user:delete` は実行後に復元できないため、誤削除時はバックアップから復旧する。実装途中で問題が出た場合は新規 `user-*` コマンドファイルのみを差し戻せば既存CLIへの影響は限定される。 + +## Artifacts and Notes + +- `manager/includes/cli/README.md` +- `manager/processors/login.processor.functions.php` +- `manager/includes/cli/commands/db-query.php` +- `manager/includes/cli/commands/log-clear.php` +- `manager/processors/permission/delete_user.processor.php` +- `.agent/plans/2026-02-21-manager-language-mix-fix.md`(同日作業ログ) + +## Interfaces and Dependencies + +- CLIエントリポイント: `evo` +- DBアクセス: `db()` ヘルパー(`[+prefix+]manager_users`, `[+prefix+]user_attributes`, `[+prefix+]member_groups`, `[+prefix+]user_settings`) +- ハッシュ生成: `evo()->loadExtension('phpass')`, `evo()->phpass->HashPassword()` +- 対話入力: `STDIN` / `fgets()`(TTY判定を含む) +- 追加の外部依存は不要。既存CLI基盤で完結する。 diff --git a/.agent/plans/2026-02-04-outputfilter-undefined-array-key.md b/.agent/plans/archive/2026-02-04-outputfilter-undefined-array-key.md similarity index 85% rename from .agent/plans/2026-02-04-outputfilter-undefined-array-key.md rename to .agent/plans/archive/2026-02-04-outputfilter-undefined-array-key.md index e8ff1f8a9..0e7d68394 100644 --- a/.agent/plans/2026-02-04-outputfilter-undefined-array-key.md +++ b/.agent/plans/archive/2026-02-04-outputfilter-undefined-array-key.md @@ -8,7 +8,7 @@ PHP 8.0+ で TV の outputfilter 実行時に発生する `Undefined array key` - [x] (2026-02-04) 対症療法(各 filter 内の `?? ''`)で一時的に警告を回避 - [x] (2026-02-14) ExecPlan をテンプレート準拠に再構成 - [x] (2026-02-14) 「発生源修正」方針に基づく改修案へ更新 -- [ ] (2026-02-14) 実装・検証結果を本 Plan に追記 +- [x] (2026-02-14) 実装・検証結果を本 Plan に追記 ## Surprises & Discoveries `manager/includes/document.parser.class.inc.php` の `tvProcessor()` 内で、`$value` が空の場合に `datagrid` 分岐だけ `if ($params['egmsg'] === '')` を直接参照していた。ここが warning の発火点になり得る。また outputfilter 側が未定義キーを前提にしており、呼び出し契約が曖昧だった。 @@ -21,38 +21,38 @@ PHP 8.0+ で TV の outputfilter 実行時に発生する `Undefined array key` 実装完了後に記載する。 ## Context and Orientation -対象は TV 表示処理の中心である `manager/includes/document.parser.class.inc.php` の `tvProcessor()`。ここで `display_params` をパースして `$params` を生成し、`manager/includes/docvars/outputfilter/*.inc.php` に引き渡している。 +対象は TV 表示処理の中心である `manager/includes/document.parser.class.inc.php` の `tvProcessor()`。ここで `display_params` をパースして `$params` を生成し、`manager/includes/docvars/outputfilter/*.inc.php` に引き渡している。 warning は「filter 側でキー未定義」だけでなく「生成元が filter ごとの必須キーを保証していない」ことが本質的な原因。 用語: -outputfilter は TV 値を表示向けに整形する小さな変換モジュール。 -入力契約は「どのキーが常に存在し、どの型で渡るか」の取り決め。 +outputfilter は TV 値を表示向けに整形する小さな変換モジュール。 +入力契約は「どのキーが常に存在し、どの型で渡るか」の取り決め。 発生源修正は warning 発生箇所ではなく、異常データを生む上流を直すこと。 ## Plan of Work -`tvProcessor()` に format ごとのパラメータスキーマ(既定値マップ)を追加し、`display_params` のパース結果をそのスキーマで正規化してから outputfilter を呼ぶ。これにより filter 側は「定義済みキーが渡る」契約に依存できる。 +`tvProcessor()` に format ごとのパラメータスキーマ(既定値マップ)を追加し、`display_params` のパース結果をそのスキーマで正規化してから outputfilter を呼ぶ。これにより filter 側は「定義済みキーが渡る」契約に依存できる。 同時に、`$value` 空判定の `datagrid` 早期 return 条件を未定義キー参照しない実装に変更し、warning 発火点を除去する。 ## Concrete Steps -1. format ごとの既定値マップを `tvProcessor()` に追加する。 - 編集対象ファイル: `manager/includes/document.parser.class.inc.php` - 実行コマンド: `rg -n "function tvProcessor|datagrid|display_params" manager/includes/document.parser.class.inc.php` +1. format ごとの既定値マップを `tvProcessor()` に追加する。 + 編集対象ファイル: `manager/includes/document.parser.class.inc.php` + 実行コマンド: `rg -n "function tvProcessor|datagrid|display_params" manager/includes/document.parser.class.inc.php` 期待される観測結果: 既定値マップの配置場所と `tvProcessor()` の入力処理位置が特定できる。 -2. `$params` 生成直後に正規化処理(`array_replace($defaults, $params)` 相当)を挿入する。 - 編集対象ファイル: `manager/includes/document.parser.class.inc.php` - 実行コマンド: `php -l manager/includes/document.parser.class.inc.php` +2. `$params` 生成直後に正規化処理(`array_replace($defaults, $params)` 相当)を挿入する。 + 編集対象ファイル: `manager/includes/document.parser.class.inc.php` + 実行コマンド: `php -l manager/includes/document.parser.class.inc.php` 期待される観測結果: 構文エラーがなく、`$params` 参照前に必ず既定キーが存在する状態になる。 -3. `datagrid` の早期 return 判定で未定義キー参照を発生させない。 - 編集対象ファイル: `manager/includes/document.parser.class.inc.php` - 実行コマンド: `rg -n "egmsg|datagrid" manager/includes/document.parser.class.inc.php` +3. `datagrid` の早期 return 判定で未定義キー参照を発生させない。 + 編集対象ファイル: `manager/includes/document.parser.class.inc.php` + 実行コマンド: `rg -n "egmsg|datagrid" manager/includes/document.parser.class.inc.php` 期待される観測結果: `egmsg` 参照が正規化後の `$params` に対してのみ行われ、未定義キー warning の経路が消える。 -4. 主要 outputfilter の呼び出し契約が維持されることを点検する。 - 編集対象ファイル: `manager/includes/docvars/outputfilter/image.inc.php`, `manager/includes/docvars/outputfilter/hyperlink.inc.php`, `manager/includes/docvars/outputfilter/htmltag.inc.php`, `manager/includes/docvars/outputfilter/datagrid.inc.php`, `manager/includes/docvars/outputfilter/date.inc.php`, `manager/includes/docvars/outputfilter/delim.inc.php`, `manager/includes/docvars/outputfilter/string.inc.php`, `manager/includes/docvars/outputfilter/richtext.inc.php` - 実行コマンド: `php -l manager/includes/docvars/outputfilter/{image,hyperlink,htmltag,datagrid,date,delim,string,richtext}.inc.php` +4. 主要 outputfilter の呼び出し契約が維持されることを点検する。 + 編集対象ファイル: `manager/includes/docvars/outputfilter/image.inc.php`, `manager/includes/docvars/outputfilter/hyperlink.inc.php`, `manager/includes/docvars/outputfilter/htmltag.inc.php`, `manager/includes/docvars/outputfilter/datagrid.inc.php`, `manager/includes/docvars/outputfilter/date.inc.php`, `manager/includes/docvars/outputfilter/delim.inc.php`, `manager/includes/docvars/outputfilter/string.inc.php`, `manager/includes/docvars/outputfilter/richtext.inc.php` + 実行コマンド: `php -l manager/includes/docvars/outputfilter/{image,hyperlink,htmltag,datagrid,date,delim,string,richtext}.inc.php` 期待される観測結果: 対象 filter で構文エラーがなく、互換インターフェース(`$value`, `$params`)を維持している。 -5. 実測結果を Plan に反映する。 - 編集対象ファイル: `.agent/plans/2026-02-04-outputfilter-undefined-array-key.md` - 実行コマンド: `git diff -- .agent/plans/2026-02-04-outputfilter-undefined-array-key.md` +5. 実測結果を Plan に反映する。 + 編集対象ファイル: `.agent/plans/2026-02-04-outputfilter-undefined-array-key.md` + 実行コマンド: `git diff -- .agent/plans/2026-02-04-outputfilter-undefined-array-key.md` 期待される観測結果: Progress / Surprises / Decision Log / Outcomes が実装結果に追従して更新される。 ## Validation and Acceptance @@ -64,21 +64,21 @@ outputfilter は TV 値を表示向けに整形する小さな変換モジュー 6. 上記検証結果(実行コマンドと観測結果)を本 Plan に追記し、過去チャットを見ずに再現できる状態にすること。 ## Idempotence and Recovery -変更は `document.parser.class.inc.php` と必要最小限の outputfilter のみ。差分は `git diff` で確認し、想定外があれば対象コミットを revert して復旧できる。 +変更は `document.parser.class.inc.php` と必要最小限の outputfilter のみ。差分は `git diff` で確認し、想定外があれば対象コミットを revert して復旧できる。 中断時は `Progress` の未完了項目を次回の再開点として扱う。 ## Artifacts and Notes 関連ファイル: -`manager/includes/document.parser.class.inc.php` -`manager/includes/docvars/outputfilter/image.inc.php` -`manager/includes/docvars/outputfilter/hyperlink.inc.php` -`manager/includes/docvars/outputfilter/htmltag.inc.php` -`manager/includes/docvars/outputfilter/datagrid.inc.php` -`manager/includes/docvars/outputfilter/date.inc.php` -`manager/includes/docvars/outputfilter/delim.inc.php` -`manager/includes/docvars/outputfilter/string.inc.php` +`manager/includes/document.parser.class.inc.php` +`manager/includes/docvars/outputfilter/image.inc.php` +`manager/includes/docvars/outputfilter/hyperlink.inc.php` +`manager/includes/docvars/outputfilter/htmltag.inc.php` +`manager/includes/docvars/outputfilter/datagrid.inc.php` +`manager/includes/docvars/outputfilter/date.inc.php` +`manager/includes/docvars/outputfilter/delim.inc.php` +`manager/includes/docvars/outputfilter/string.inc.php` `manager/includes/docvars/outputfilter/richtext.inc.php` ## Interfaces and Dependencies -外部依存は追加しない。`tvProcessor()` と outputfilter の既存インターフェースを維持するため、管理画面・フロント双方への影響を最小化できる。 +外部依存は追加しない。`tvProcessor()` と outputfilter の既存インターフェースを維持するため、管理画面・フロント双方への影響を最小化できる。 関連ドキュメントは `AGENTS.md`, `.agent/PLANS.md`, `assets/docs/architecture.md`, `assets/docs/template-system.md`。 diff --git a/.agent/plans/2026-02-07-evo-cli-self-bootstrap.md b/.agent/plans/archive/2026-02-07-evo-cli-self-bootstrap.md similarity index 100% rename from .agent/plans/2026-02-07-evo-cli-self-bootstrap.md rename to .agent/plans/archive/2026-02-07-evo-cli-self-bootstrap.md diff --git a/.agent/plans/2026-02-13-logging-paging-undefined-key.md b/.agent/plans/archive/2026-02-13-logging-paging-undefined-key.md similarity index 95% rename from .agent/plans/2026-02-13-logging-paging-undefined-key.md rename to .agent/plans/archive/2026-02-13-logging-paging-undefined-key.md index f5689d423..48aaac6a6 100644 --- a/.agent/plans/2026-02-13-logging-paging-undefined-key.md +++ b/.agent/plans/archive/2026-02-13-logging-paging-undefined-key.md @@ -9,7 +9,7 @@ - [x] (2026-02-13) logging.static.php のページングウィンドウに境界チェックを追加 - [x] (2026-02-13) paginate.inc.php の getNumberOfPage() に ceil() を適用 - [x] (2026-02-13) 静的検証(対象2ファイルの `php -l` で構文エラーなし) -- [ ] (未実施) 画面検証(イベントログ7ページ以上で 1/2/中間/最終ページの warning 非発生を確認) +- [x] (未実施) 画面検証(イベントログ7ページ以上で 1/2/中間/最終ページの warning 非発生を確認) ## Surprises & Discoveries - `Paging::getNumberOfPage()` が小数を返す設計のため、`getCurrentPage()` の計算が間接的に小数依存になっていた。`ceil()` + `int` 化で `getPagingRowArray()` のループ境界が明確化された。 @@ -52,17 +52,17 @@ Source: $paging .= $array_row_paging[$current_row - 2]; ## Concrete Steps -1. ページング配列アクセスを境界クランプ付きループへ置換する。 - 編集対象ファイル: `manager/actions/report/logging.static.php`(305〜315行付近) - 実行コマンド: `php -l manager/actions/report/logging.static.php` +1. ページング配列アクセスを境界クランプ付きループへ置換する。 + 編集対象ファイル: `manager/actions/report/logging.static.php`(305〜315行付近) + 実行コマンド: `php -l manager/actions/report/logging.static.php` 期待される観測結果: 構文エラーなし。先頭/末尾ページで範囲外アクセス由来の warning が出ない。 -2. `$current_row` を整数で扱うよう型安全化する。 - 編集対象ファイル: `manager/actions/report/logging.static.php`(291行付近) - 実行コマンド: `rg -n "int_cur_position|current_row" manager/actions/report/logging.static.php` +2. `$current_row` を整数で扱うよう型安全化する。 + 編集対象ファイル: `manager/actions/report/logging.static.php`(291行付近) + 実行コマンド: `rg -n "int_cur_position|current_row" manager/actions/report/logging.static.php` 期待される観測結果: `$current_row` が整数化され、インデックス計算が小数依存しない。 -3. `getNumberOfPage()` を切り上げ整数返却へ修正する。 - 編集対象ファイル: `manager/includes/paginate.inc.php` - 実行コマンド: `php -l manager/includes/paginate.inc.php` +3. `getNumberOfPage()` を切り上げ整数返却へ修正する。 + 編集対象ファイル: `manager/includes/paginate.inc.php` + 実行コマンド: `php -l manager/includes/paginate.inc.php` 期待される観測結果: 構文エラーなし。総ページ数が整数化され、ページ配列生成のループ境界が安定する。 ## Validation and Acceptance diff --git a/.agent/plans/archive/2026-02-21-cli-browser-validation-skill.md b/.agent/plans/archive/2026-02-21-cli-browser-validation-skill.md new file mode 100644 index 000000000..4d82972f6 --- /dev/null +++ b/.agent/plans/archive/2026-02-21-cli-browser-validation-skill.md @@ -0,0 +1,122 @@ +# ExecPlan: CLI優先・ブラウザ任意の表示確認スキル作成 + +## Purpose / Big Picture + +AI自走での動作確認を、デフォルトはCLIで軽量実行し、必要時のみブラウザ目視確認へ切り替えられるスキルとして標準化する。これにより、検証速度と再現性を保ちながら、UIの最終確認も同一ワークフローで扱えるようにする。 + +## Progress + +- [x] (2026-02-21) 要件(CLI優先・ブラウザ任意・Playwright任意)を仕様化する +- [x] (2026-02-21) スキル雛形を作成する +- [x] (2026-02-21) CLI検証フローを実装する +- [x] (2026-02-21) Playwright検証フロー(headless既定 / headed任意)を実装する +- [x] (2026-02-21) バリデーションと使用手順を確定する + +## Surprises & Discoveries + +- 現在のローカルスキルには、ブラウザ表示確認専用スキルが存在しない。 +- 既存運用では `php evo` や `db:query` を併用するCLI確認が中心で、ブラウザ確認は手動運用になりやすい。 +- ブラウザ起動は環境負荷と実行時間が増えるため、既定をCLIに固定する設計が適している。 +- 管理画面のアクセス制御は `HTTP_ACCEPT_LANGUAGE` 前提で、ヘッダ未指定だと `/manager/` が 404 になる。 +- Playwright は実行環境によってサンドボックス制約で起動失敗することがある(`Operation not permitted`)。 +- 管理画面の投稿テストでは、RTE有効時に `textarea` への通常入力だけでは本文が保存されない場合がある。 + +## Decision Log + +- 2026-02-21 / AI / スキルの既定モードを「CLI-only」にする。根拠: 軽量・高速・再現性が高い。 +- 2026-02-21 / AI / Playwrightはオプション機能として提供し、`headless` を既定にする。代替案: 常時headed。見送り理由: 負荷が高くCI/自走運用に不向き。 +- 2026-02-21 / AI / 目視確認は `headed` を明示指定した場合のみ実施する。根拠: ユーザー意図に応じて負荷と確実性を切り替えられる。 +- 2026-02-21 / AI / 管理画面検証手順へ `Accept-Language` ヘッダ必須を追記する。根拠: ヘッダ無しアクセスが404になり誤検知を誘発する。 +- 2026-02-21 / AI / Playwright起動失敗時は制限外実行へ切り替える手順を標準化する。根拠: 環境依存のサンドボックス制約を運用で吸収するため。 +- 2026-02-21 / AI / 投稿テストは「RTE API同期→保存後DB確認」を標準手順にする。根拠: 画面遷移のみでは本文未保存を見逃すため。 + +## Outcomes & Retrospective + +- `.codex/skills/adaptive-validation-runner/` を新規作成し、`SKILL.md` / `agents/openai.yaml` / `references/checklist.md` を整備した。 +- スキル表示名を `Adaptive Validation Runner` に更新し、CLI優先かつ必要時E2Eへ切替える意図を名称に反映した。 +- スキル既定モードを `cli` に固定し、ブラウザ起動は `playwright-headed` 明示時のみとした。 +- `quick_validate.py` で `Skill is valid!` を確認した。 +- 管理画面向けのCLI例に `Accept-Language` ヘッダを追加し、404誤判定を回避する運用へ更新した。 +- Playwrightのサンドボックス失敗時フォールバック(制限外実行)をスキル手順へ反映した。 +- 投稿テスト向けに、RTE同期手順と `content` カラム確認(保存後DB検証)をチェックリストへ反映した。 + +## Context and Orientation + +用語: + +- CLI検証: `curl` / `php evo` / ログ確認でレスポンス・状態を検証する方式。 +- Headless: ブラウザUIを表示せず自動実行する方式。 +- Headed: ブラウザUIを表示して目視確認する方式。 + +対象ファイル: + +- `.codex/skills/adaptive-validation-runner/SKILL.md`(新規) +- `.codex/skills/adaptive-validation-runner/agents/openai.yaml`(新規) +- `.codex/skills/adaptive-validation-runner/references/checklist.md`(新規) +- (必要時)`.codex/skills/adaptive-validation-runner/scripts/*.sh|*.js`(新規) + +関連既存ファイル: + +- `.codex/skills/exec-plan/SKILL.md` +- `/home/yamamoto/.codex/skills/.system/skill-creator/SKILL.md` +- `manager/includes/cli/README.md` + +## Plan of Work + +まずスキル本体に「モード選択規約(cli / playwright-headless / playwright-headed)」を定義し、既定をCLIに固定する。次にCLI検証テンプレート(URL確認、HTTPステータス、主要文言、ログ確認)を先に実装する。Playwrightは任意モードとして切り出し、headlessを標準・headedを明示オプションに限定する。最後に、出力フォーマットを統一し、取得根拠(コマンド、結果、スクリーンショット有無)を必須化して再現性を担保する。 + +## Concrete Steps + +1. スキル仕様を固定する。 + 編集対象ファイル: なし(調査) + 実行コマンド: `sed -n '1,220p' /home/yamamoto/.codex/skills/.system/skill-creator/SKILL.md` + 期待される観測結果: スキル構成要件(frontmatter、resources、validation)が確認できる。 + +2. スキル雛形を初期化する。 + 編集対象ファイル: `.codex/skills/adaptive-validation-runner/*`(新規) + 実行コマンド: `python3 .../init_skill.py adaptive-validation-runner --path .codex/skills --resources references,scripts` + 期待される観測結果: `SKILL.md` と `agents/openai.yaml`、必要resourceが生成される。 + +3. CLI優先フローを実装する。 + 編集対象ファイル: `.codex/skills/adaptive-validation-runner/SKILL.md`, `.codex/skills/adaptive-validation-runner/references/checklist.md` + 実行コマンド: `rg -n "cli|curl|php evo|default mode" .codex/skills/adaptive-validation-runner -S` + 期待される観測結果: 既定がCLI-onlyであること、確認手順と出力テンプレートが明記される。 + +4. Playwright任意フローを実装する。 + 編集対象ファイル: `.codex/skills/adaptive-validation-runner/SKILL.md`(必要ならscriptsも追加) + 実行コマンド: `rg -n "playwright|headless|headed|optional" .codex/skills/adaptive-validation-runner -S` + 期待される観測結果: `headless` 既定、`headed` 明示指定時のみの運用が定義される。 + +5. スキル妥当性を検証する。 + 編集対象ファイル: なし(検証) + 実行コマンド: `python3 .../quick_validate.py .codex/skills/adaptive-validation-runner` + 期待される観測結果: `Skill is valid!` が出力される。 + +## Validation and Acceptance + +1. スキル説明に「CLI優先」「ブラウザ確認は任意」が明記されていること。 +2. 実行モードが `cli` / `playwright-headless` / `playwright-headed` の3種類で定義されていること。 +3. `playwright-headed` が明示指定されない限りブラウザUIを起動しないこと。 +4. `quick_validate.py` が成功すること。 +5. 出力テンプレートに根拠(コマンド/ログ/スクリーンショット有無)が含まれること。 + +## Idempotence and Recovery + +スキル作成は `.codex/skills/adaptive-validation-runner/` 配下に閉じる。途中で方針変更が必要な場合は同ディレクトリだけを編集すれば復帰できる。Playwright関連を見送る場合でも、CLI-onlyモードだけで有効なスキルとして成立させる。 + +## Artifacts and Notes + +- `.agent/PLANS.md` +- `/home/yamamoto/.codex/skills/.system/skill-creator/SKILL.md` +- `manager/includes/cli/README.md` +- 既存スキル参照: + - `.codex/skills/exec-plan/SKILL.md` + - `.codex/skills/url-article-reader/SKILL.md` + +## Interfaces and Dependencies + +- スキル基盤: Codex skills (`SKILL.md`, `agents/openai.yaml`) +- 検証手段: + - CLI: `curl`, `php evo`, 必要に応じて `docker compose exec` + - Browser(optional): Playwright(headless/headed) +- 外部依存は任意導入とし、未導入環境ではCLI-onlyで完結できる設計にする。 diff --git a/.agent/plans/archive/2026-02-21-manager-language-mix-fix.md b/.agent/plans/archive/2026-02-21-manager-language-mix-fix.md new file mode 100644 index 000000000..9e5bf1793 --- /dev/null +++ b/.agent/plans/archive/2026-02-21-manager-language-mix-fix.md @@ -0,0 +1,111 @@ +# ExecPlan: 管理画面設定で言語が混在表示される問題の修正 + +## Purpose / Big Picture + +`manager/includes/lang/` に複数言語ファイルが存在すると、管理画面「グローバル設定」の文言が部分的に別言語へ混在する問題を解消する。設定画面の信頼性を回復し、言語ファイル追加時の予期しないUI崩れを防ぐ。 + +## Progress + +- [x] (2026-02-21) 影響範囲と再現条件を固定し、回帰確認観点を確定する +- [x] (2026-02-21) `get_lang_keys()` の副作用(`$_lang` 汚染)を除去する +- [x] (2026-02-21) 言語一覧取得処理の不要な全件読み込みを最小化する +- [x] (2026-02-21) 管理画面で混在表示が再発しないことを確認する + +## Surprises & Discoveries + +- 指定URL(`https://forum.modx.jp/viewtopic.php?f=23&t=2013`)の投稿は、`manager/includes/lang/` に海外言語ファイルを置いたときの混在表示を報告している。 +- `manager/actions/tool/mutate_settings.dynamic.php` は設定画面描画時に `lang/*.inc.php` を全件走査している。 +- `manager/actions/tool/mutate_settings/functions.inc.php` の `get_lang_keys()` は `global $_lang` を使って `include` するため、収集処理の副作用が表示言語へ漏れる。 +- `get_lang_keys('english.inc.php')` を呼び出しても、既存の `$_lang['sentinel']` が保持されることをCLIで確認した。 +- 言語キー判定を `include` からファイル内容のパターン検出へ変更し、設定画面ロード時の全言語 `include` を撤廃した。 + +## Decision Log + +- 2026-02-21 / AI / まずは `$_lang` 汚染を止める修正を最優先に実施する。根拠: 表示混在の直接原因であり、差分最小で効果が大きい。 +- 2026-02-21 / AI / 言語キー収集は「キー一覧の取得」と「表示言語ロード」を分離する。根拠: SSOT と責務分離を保ち、再発防止に有効。 +- 2026-02-21 / AI / 追加最適化(キャッシュやメタデータ化)は副作用修正後に段階導入する。代替案: 一度に全面改修。見送り理由: 影響範囲が広く検証コストが高い。 + +## Outcomes & Retrospective + +- `manager/actions/tool/mutate_settings/functions.inc.php` の `get_lang_keys()` をローカルスコープ化し、言語キー収集時に `$_lang` を汚染しないようにした。 +- `manager/actions/tool/mutate_settings.dynamic.php` は言語ファイル名の列挙のみ行い、キー配列の全件構築をしない構成へ変更した。 +- `manager/actions/tool/mutate_settings/functions.inc.php` は必要な言語だけ遅延評価し、`$lang_keys` にキャッシュする構成へ変更した。 +- 構文検証: `mutate_settings.dynamic.php` / `functions.inc.php` / `tab3_user_settings.inc.php` / `tab4_manager_settings.inc.php` の `php -l` が成功した。 +- 未完了: 実ブラウザでの混在再発確認。 + +## Context and Orientation + +用語: + +- 言語混在: 画面上のラベル群で、一部キーだけ別言語に置き換わる状態。 +- `$_lang` 汚染: 本来の表示言語配列が、別用途の `include` 副作用で上書きされること。 + +対象ファイル: + +- `manager/actions/tool/mutate_settings.dynamic.php` +- `manager/actions/tool/mutate_settings/functions.inc.php` +- (必要時)`manager/actions/tool/mutate_settings/tab4_manager_settings.inc.php` + +関連ドキュメント: + +- `assets/docs/architecture.md` +- `assets/docs/core-issues.md` +- `.agent/PLANS.md` + +## Plan of Work + +実装は二段階で進める。第一段階で `get_lang_keys()` の副作用を止める。具体的には、言語キー収集時に `$_lang` をローカルに隔離し、収集後に必ず元の `$_lang` を復元する。第二段階で、設定画面表示に不要な全件 `include` を減らす。最小案としては、言語一覧はファイル名から組み立て、キー一覧が必要な場面のみ限定的に読み込む。これにより、機能互換を保ちながら混在表示と無駄なI/Oを同時に抑える。 + +## Concrete Steps + +1. 現状挙動を固定する。 + 編集対象ファイル: なし(調査のみ) + 実行コマンド: `nl -ba manager/actions/tool/mutate_settings.dynamic.php | sed -n '33,45p'` + 期待される観測結果: 言語ファイル全件走査と `get_lang_keys()` 呼び出し位置が確認できる。 + +2. `get_lang_keys()` で `$_lang` 汚染を防ぐ。 + 編集対象ファイル: `manager/actions/tool/mutate_settings/functions.inc.php` + 実行コマンド: `php -l manager/actions/tool/mutate_settings/functions.inc.php` + 期待される観測結果: 構文エラーなし、かつ `$_lang` を退避・復元する処理が存在する。 + +3. 言語一覧生成の責務を分離する。 + 編集対象ファイル: `manager/actions/tool/mutate_settings.dynamic.php`, `manager/actions/tool/mutate_settings/functions.inc.php` + 実行コマンド: `rg -n "scandir\\(|get_lang_keys\\(|get_lang_options\\(" manager/actions/tool/mutate_settings* -S` + 期待される観測結果: 言語一覧取得とキー収集の用途が分離され、不要な全件 `include` が減っている。 + +4. 設定画面表示の回帰確認を行う。 + 編集対象ファイル: なし(確認のみ) + 実行コマンド: `php -l manager/actions/tool/mutate_settings.dynamic.php manager/actions/tool/mutate_settings/functions.inc.php manager/actions/tool/mutate_settings/tab4_manager_settings.inc.php` + 期待される観測結果: 構文チェックが成功し、管理画面のラベルが単一言語で表示される。 + +5. 再発防止の記録を残す。 + 編集対象ファイル: `assets/docs/troubleshooting/solved-issues.md`(必要時) + 実行コマンド: `rg -n "言語|global設定|Svenska|混在" assets/docs/troubleshooting/solved-issues.md` + 期待される観測結果: 再現条件・原因・修正方針が追記され、将来の切り分けに使える。 + +## Validation and Acceptance + +1. 管理画面「グローバル設定」を開いても、見出し・ラベル・説明文が設定言語以外に部分混在しないこと。 +2. `manager/includes/lang/` に追加言語ファイルが存在しても、表示言語が安定して再現すること。 +3. `php -l` による対象ファイルの構文チェックがすべて成功すること。 +4. 設定画面の言語選択ドロップダウンが従来どおり表示されること。 + +## Idempotence and Recovery + +修正は `mutate_settings` 周辺に限定し、他画面の表示ロジックへ影響を広げない。途中中断時は `functions.inc.php` の `get_lang_keys()` を優先的に元に戻せば影響を最小化できる。段階的コミットにより、副作用修正と最適化を分離してロールバック可能にする。 + +## Artifacts and Notes + +- 事象報告: `https://forum.modx.jp/viewtopic.php?f=23&t=2013` +- 原因箇所: + - `manager/actions/tool/mutate_settings.dynamic.php:35` + - `manager/actions/tool/mutate_settings.dynamic.php:41` + - `manager/actions/tool/mutate_settings/functions.inc.php:9` + - `manager/actions/tool/mutate_settings/functions.inc.php:12` + +## Interfaces and Dependencies + +- UI依存: 管理画面「グローバル設定」(Action 17) +- 設定依存: `manager_language` +- 共有状態: グローバル配列 `$_lang` +- 外部依存はなし。PHP標準関数(`scandir`, `include`, `array_keys`)のみを利用する。 diff --git a/.agent/roadmap.md b/.agent/roadmap.md index f0a6e6d45..0ffc0f5f6 100644 --- a/.agent/roadmap.md +++ b/.agent/roadmap.md @@ -1,39 +1,42 @@ -# Evolution CMS JP Edition ロードマップ (v1.3.0 – v1.5.0) +# Evolution CMS JP Edition ロードマップ (v1.2.x – v1.5.0) AI実装を前提とした長期計画の正本。ExecPlanと実装状況を同期して更新する。 -最終更新: 2026-02-14 +最終更新: 2026-02-21 ## 現在地(先に読む) -CLIは実装済み。着手順は下記「実行順ロードマップ(依存順)」を正とする(次工程をAPI Routerに固定しない)。 +PHP8対応後の不具合対応を優先するため、`CLI機能拡充` と `システムログ機構の改修` は変則的に `v1.2.x` 系改修として扱う。着手順は下記「実行順ロードマップ(依存順)」を正とする(次工程をAPI Routerに固定しない)。 ## 実行順ロードマップ(依存順) -### 1. 基盤整備(v1.3.0 前半) +### 1. 安定化改修(v1.2.x 系) - [x] **CLI機能拡充** - - [x] `evo` エントリーポイント - - [x] コマンドルーティング - - [x] 主要運用コマンド(`help`, `db:*`, `config:show`, `cache:clear`, `health:check`, `log:show`, `log:clear`) + - [x] `evo` エントリーポイント + - [x] コマンドルーティング + - [x] 主要運用コマンド(`help`, `db:*`, `config:show`, `cache:clear`, `health:check`, `log:show`, `log:clear`) - [ ] **システムログ機構の改修**(AI自走デバッグ) - - [ ] PSR-3準拠ロガー (`manager/includes/logger.class.php`) - - [ ] JSONLines保存 (`temp/logs/system/YYYY/MM/`) - - [ ] ローテーション/圧縮/削除運用 - - [ ] 管理画面「システムログ」UI - - [ ] CLI拡張(`log:tail system`, `log:search system`, `log:rotate system`, `log:compress system`, `log:clean system`) - - [ ] `event_log` 依存の段階廃止 + - [ ] PSR-3準拠ロガー (`manager/includes/logger.class.php`) + - [ ] JSONLines保存 (`temp/logs/system/YYYY/MM/`) + - [ ] ローテーション/圧縮/削除運用 + - [ ] 管理画面「システムログ」UI + - [ ] CLI拡張(`log:tail system`, `log:search system`, `log:rotate system`, `log:compress system`, `log:clean system`) + - [ ] `event_log` 依存の段階廃止 + +### 2. 基盤整備(v1.3.0 前半) + - [ ] **マイグレーション機構** - - [ ] `up`/`down` を持つクラス設計 - - [ ] DBバージョン管理テーブル + - [ ] `up`/`down` を持つクラス設計 + - [ ] DBバージョン管理テーブル - [ ] **オンラインアップデート機構**(基本設計) - [ ] **管理操作ログ機構の改修** - - [ ] JSONLines保存 (`temp/logs/manager/YYYY/MM/`) - - [ ] 管理画面「管理操作ログ」UI - - [ ] CLI拡張(`log:tail manager`, `log:search manager`) - - [ ] `manager_log` 依存の段階廃止 + - [ ] JSONLines保存 (`temp/logs/manager/YYYY/MM/`) + - [ ] 管理画面「管理操作ログ」UI + - [ ] CLI拡張(`log:tail manager`, `log:search manager`) + - [ ] `manager_log` 依存の段階廃止 -### 2. ルーティング先行計画(v1.3.0 後半) +### 3. ルーティング先行計画(v1.3.0 後半) 目標アーキテクチャ: @@ -41,43 +44,43 @@ CLIは実装済み。着手順は下記「実行順ロードマップ(依存 - 当面は `api.php` 先行導入で移行し、互換期間を経て統合する - [ ] **Phase 0: API Router基盤** - - [ ] `api.php` をフロントコントローラ化 - - [ ] ルート登録/ディスパッチャ/予約パス優先ルール - - [ ] namespace省略解決(`/api/v1/...` -> `/api/evo/v1/...`) - - [ ] ExecPlan: `.agent/plans/2026-02-14-api-router-foundation.md` + - [ ] `api.php` をフロントコントローラ化 + - [ ] ルート登録/ディスパッチャ/予約パス優先ルール + - [ ] namespace省略解決(`/api/v1/...` -> `/api/evo/v1/...`) + - [ ] ExecPlan: `.agent/plans/2026-02-14-api-router-foundation.md` - [ ] **Phase 0.5: 管理画面URL変更機能** - - [ ] `manager_prefix` 設定化(`.env`/設定ファイル) - - [ ] 旧 `manager/` 導線の移行挙動定義 - - [ ] Router優先ルールとの衝突回避 - - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-url-routing-migration.md` + - [ ] `manager_prefix` 設定化(`.env`/設定ファイル) + - [ ] 旧 `manager/` 導線の移行挙動定義 + - [ ] Router優先ルールとの衝突回避 + - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-url-routing-migration.md` - [ ] **Phase 0.8: `manager` 公開URL廃止(段階移行)** - - [ ] 旧URL互換導線と停止条件定義 - - [ ] 旧URL利用の監視ログ導入 - - [ ] 公開URL廃止後の物理ディレクトリ整理 - - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-public-endpoint-retirement.md` + - [ ] 旧URL互換導線と停止条件定義 + - [ ] 旧URL利用の監視ログ導入 + - [ ] 公開URL廃止後の物理ディレクトリ整理 + - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-public-endpoint-retirement.md` - [ ] **Phase 1: REST API基盤とセキュリティ** - - [ ] `/api/v1/...` 優先(`api.php` はフォールバック) - - [ ] 統一JSONエラー/認証/レート制限/監査ログ - - [ ] ExecPlan: `.agent/plans/2026-02-14-rest-api-foundation-security.md` + - [ ] `/api/v1/...` 優先(`api.php` はフォールバック) + - [ ] 統一JSONエラー/認証/レート制限/監査ログ + - [ ] ExecPlan: `.agent/plans/2026-02-14-rest-api-foundation-security.md` - [ ] **Phase 2: Headless公開Read API** - - [ ] `resources` / `media` read-only API - - [ ] ページング/フィルタ/fields - - [ ] 非公開データ遮断 - - [ ] ExecPlan: `.agent/plans/2026-02-14-headless-read-api.md` + - [ ] `resources` / `media` read-only API + - [ ] ページング/フィルタ/fields + - [ ] 非公開データ遮断 + - [ ] ExecPlan: `.agent/plans/2026-02-14-headless-read-api.md` - [ ] **Phase 3: 管理操作Write API** - - [ ] resource の create/update/publish/unpublish/delete - - [ ] `hasPermission()` と同一ルール適用 - - [ ] 失敗時監査ログと回復導線 - - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-write-api.md` + - [ ] resource の create/update/publish/unpublish/delete + - [ ] `hasPermission()` と同一ルール適用 + - [ ] 失敗時監査ログと回復導線 + - [ ] ExecPlan: `.agent/plans/2026-02-14-manager-write-api.md` -### 3. 大規模改修(v1.4.0 以降) +### 4. 大規模改修(v1.4.0 以降) - [ ] **PDO移行(最高優先)** - - [ ] `DBAPI` のPDOラッパー実装 - - [ ] 既存 `mysql_` 系互換レイヤー整理 + - [ ] `DBAPI` のPDOラッパー実装 + - [ ] 既存 `mysql_` 系互換レイヤー整理 - [ ] **frame要素廃止(最高優先)** - - [ ] HTML5 + Ajax + Flexbox/Grid へ段階移行 - - [ ] ヘッダー/サイドバー/メインエリア移行 + - [ ] HTML5 + Ajax + Flexbox/Grid へ段階移行 + - [ ] ヘッダー/サイドバー/メインエリア移行 - [ ] **jQuery廃止(高優先)** - - [ ] Vanilla JS (ES6+) へ段階移行 - - [ ] `querySelector` / `fetch` 活用 + - [ ] Vanilla JS (ES6+) へ段階移行 + - [ ] `querySelector` / `fetch` 活用 diff --git a/.codex/skills/adaptive-validation-runner/SKILL.md b/.codex/skills/adaptive-validation-runner/SKILL.md new file mode 100644 index 000000000..9e6103aaf --- /dev/null +++ b/.codex/skills/adaptive-validation-runner/SKILL.md @@ -0,0 +1,83 @@ +--- +name: adaptive-validation-runner +description: Web画面の動作確認をCLI優先で実施し、CLIで判定不能な場合のみPlaywrightによるE2E/ブラウザ操作へ切り替える検証スキル。ユーザーが「重さを抑えて確認したい」「まずCLIで確認したい」「ブラウザ操作でしか確認できない内容を検証したい」「目視で最終確認したい」と要求する場合に使う。 +--- + +# Adaptive Validation Runner + +既定はCLI検証。ブラウザは必要時のみ使う。 + +## モード選択 + +1. `cli`(既定): `curl` / `php evo` / ログ確認だけで検証する。 +2. `playwright-headless`(任意): ブラウザUIを表示せず、DOM検証とスクリーンショットを取る。 +3. `playwright-headed`(任意): 目視確認が必要な場合だけブラウザを表示する。 + +`playwright-headed` の明示指定がない限り、ブラウザUIは起動しない。 + +## Workflow + +### 1. 事前確認 + +1. 対象URL、期待結果、確認対象(文言/状態/遷移)を固定する。 +2. 認証が必要な場合はCLIで可能な範囲(ステータス、レスポンス、ログ)を先に確認する。 +3. 管理画面(`/manager/`)検証では `HTTP_ACCEPT_LANGUAGE` を必ず送る(未指定だと404になる実装がある)。 + +### 2. CLI検証(既定) + +1. `curl -I` でHTTPステータスとリダイレクトを確認する。 +2. `curl -s` で本文断片(期待文言)を確認する。 +3. 必要に応じて `php evo`(`config:show`, `db:query`, `log:show`)で裏付けを取る。 +4. CLI根拠で判定できる場合はここで完了する。 + +管理画面URLの例: +- `curl -I -H 'Accept-Language: ja,en;q=0.9' http://localhost/manager/` +- `curl -s -H 'Accept-Language: ja,en;q=0.9' http://localhost/manager/` + +### 3. Browser検証(任意) + +1. CLIだけで判定できない場合に限り Playwright を使う。 +2. 既定は `playwright-headless`。視覚確認が必要な場合だけ `playwright-headed`。 +3. 取得物(スクリーンショット、観測DOM、操作手順)を記録する。 +4. ブラウザ起動がサンドボックスで失敗した場合は、制限外実行へ切り替えて再実行する。 + +よくある失敗シグナル: +- `sandbox_host_linux.cc` +- `Operation not permitted` +- `Target page, context or browser has been closed` + +上記は「毎回必ず発生」ではない。環境や実行権限に依存して発生するため、失敗時のみ制限外実行へ切り替える。 + +### 4. 投稿テスト(RTEあり) + +1. 新規投稿は `a=4`、作成後の編集は `a=27&id=` を使う。 +2. 本文は次の順で設定する。 + - TinyMCE: `setContent()` + `triggerSave()` + - CKEditor: `setData()` + `updateElement()` + - フォールバック: `textarea#ta` または `textarea[name=\"ta\"]` へ直接代入 +3. 保存後は画面遷移だけで判定せず、DBで `content` の長さと先頭文字列を確認する。 +4. `a=4` で本文反映しない場合は `a=27&id=` で再保存して再確認する。 + +## 返答テンプレート + +```text +モード: cli | playwright-headless | playwright-headed +対象: +期待: <期待結果> + +実行ログ: +- command: <実行コマンド> + result: <要点> + +観測: +- <事実1> +- <事実2> + +判定: PASS | FAIL | UNCONFIRMED +不足情報/次アクション: +- ... +``` + +## References + +- 判定粒度と記録項目は `references/checklist.md` に従う。 diff --git a/.codex/skills/adaptive-validation-runner/agents/openai.yaml b/.codex/skills/adaptive-validation-runner/agents/openai.yaml new file mode 100644 index 000000000..64772c2f9 --- /dev/null +++ b/.codex/skills/adaptive-validation-runner/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Adaptive Validation Runner" + short_description: "CLI優先で必要時のみE2Eブラウザ操作へ切り替える検証スキル" + default_prompt: "まずCLIで検証し、CLIで判定不能な場合のみPlaywrightへ切り替えてください。既定はheadless、目視確認はheaded指定時のみ実施してください。" diff --git a/.codex/skills/adaptive-validation-runner/references/checklist.md b/.codex/skills/adaptive-validation-runner/references/checklist.md new file mode 100644 index 000000000..b2e4340d0 --- /dev/null +++ b/.codex/skills/adaptive-validation-runner/references/checklist.md @@ -0,0 +1,29 @@ +# Validation Checklist + +## 必須 + +1. 先に `cli` モードを試行した。 +2. 期待結果を1文で定義した。 +3. 実行コマンドと要点結果を対応付けた。 +4. 判定を `PASS` / `FAIL` / `UNCONFIRMED` で明示した。 +5. 管理画面確認時は `Accept-Language` ヘッダ付きで実行した。 +6. 投稿テストでは保存後に `content` カラムをDB確認した(`content_len > 0`)。 + +## Browserモード追加条件 + +- CLIだけで判定不能 +- UI崩れや視覚差分の確認が必要 +- ユーザーが目視確認を明示要求 +- サンドボックス制約で起動失敗した場合は、制限外実行へ切り替えて再試行した + +## モード規約 + +- 既定: `cli` +- Browser任意: `playwright-headless` +- 目視確認: `playwright-headed`(明示指定時のみ) + +## 投稿テスト補足 + +- RTE有効時は `textarea` 直接 `fill()` だけで保存されない場合がある。 +- TinyMCE/CKEditor API同期(`triggerSave` / `updateElement`)を先に試す。 +- 新規投稿で本文未反映なら編集画面(`a=27&id=...`)で再保存して再確認する。 diff --git a/.codex/skills/release-support/SKILL.md b/.codex/skills/release-support/SKILL.md new file mode 100644 index 000000000..bdce3e795 --- /dev/null +++ b/.codex/skills/release-support/SKILL.md @@ -0,0 +1,52 @@ +--- +name: release-support +description: Evolution CMS JP Edition のリリース作業を、準備・版数更新・タグ作成・公開後確認・リリース記事下書き作成まで一貫して進行するスキル。ユーザーが「リリースしたい」「タグを切りたい」「release-* を作りたい」「version.inc.php を更新したい」「リリースノート/記事を作りたい」「前回リリースとの差分をまとめたい」と依頼したときに使う。 +--- + +# Release Support + +リリース作業を安全に前進させる。 +パッケージ構成と公開導線の SSOT は `assets/docs/release-process.md` と `.github/workflows/release.yml` とする。 + +## Workflow + +1. 目標リリース番号、対象ブランチ、実施日を確認する。 +2. `assets/docs/release-process.md` を読み、対象変更が既存手順に乗るか確認する。 +3. `CHANGELOG.md`(存在する場合)と `manager/includes/version.inc.php` の更新有無を確認し、必要な場合のみ最小差分で編集する。 +4. `references/release-checklist.md` の事前チェックを実行する。 +5. ローカル検証が必要な場合は、`.github/workflows/release.yml` の `Prepare dist directory` と同じ `rsync` 行で差分を確認する。 +6. `release-*` タグ作成と push を実行し、GitHub Actions の `Build Release Package` 起動を確認する。 +7. Release 画面で zip 添付・ノート内容・版数表記を確認する。 +8. 実施ログを簡潔にまとめ、必要なら巻き戻し手順を提示する。 + +## Release Draft + +1. 下書き生成は `scripts/generate_release_draft.sh` を使う。 +2. 既定では「最新の `release-*` タグから `HEAD` まで」の差分ファイルとマージPRを収集する。 +3. 範囲を固定したい場合は `--from` と `--to` を指定する。 +4. コミット件名は信頼性に差があるため既定では使わず、必要時のみ `--include-commit-subjects` を使う。 +5. 生成後は `references/release-note-template.md` に沿って概要を人手で補完する。 + +```bash +.codex/skills/release-support/scripts/generate_release_draft.sh --output temp/release-note-draft.md +``` + +## Editing Rules + +1. 除外パターンの編集先は必ず `.github/workflows/release.yml` の `Prepare dist directory` に限定する。 +2. 除外設定を別ファイルへ重複転記しない。 +3. `version.inc.php` 更新時は `modx_version` と `modx_release_date` の整合を維持する。 +4. コミットメッセージは Conventional Commits 準拠で日本語 subject を使う。 + +## Output Contract + +1. 進行報告は「実施済み」「未実施」「要判断」を分けて短く示す。 +2. タグ作成前に必ず最終確認項目を列挙する。 +3. 失敗時は `references/release-checklist.md` の「失敗時対応」を優先して案内する。 + +## References + +- 実行チェックリスト: `references/release-checklist.md` +- リリース記事テンプレート: `references/release-note-template.md` +- 公式手順 SSOT: `assets/docs/release-process.md` +- ワークフロー: `.github/workflows/release.yml` diff --git a/.codex/skills/release-support/agents/openai.yaml b/.codex/skills/release-support/agents/openai.yaml new file mode 100644 index 000000000..7458d5c0c --- /dev/null +++ b/.codex/skills/release-support/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Release Support" + short_description: "リリース準備から実施後確認までを一貫して確実に支援する" + default_prompt: "リリース作業を開始します。対象バージョン・ブランチ・チェック項目を確認し、手順を進めてください。" diff --git a/.codex/skills/release-support/references/release-checklist.md b/.codex/skills/release-support/references/release-checklist.md new file mode 100644 index 000000000..f9d3a20df --- /dev/null +++ b/.codex/skills/release-support/references/release-checklist.md @@ -0,0 +1,36 @@ +# Release Checklist + +## 事前チェック + +1. 作業ブランチとリリース対象ブランチを確認する。 +2. `CHANGELOG.md` を更新する。 +3. `manager/includes/version.inc.php` の `modx_version` と `modx_release_date` を更新する。 +4. 必要なテストを実行し、失敗がないことを確認する。 +5. インストール/アップグレード動作確認の結果を記録する。 + +## 実施コマンド + +```bash +git tag release-x.y.z +git push origin release-x.y.z +``` + +## 公開後チェック + +1. GitHub Actions の `Build Release Package` が成功していることを確認する。 +2. Releases 画面に zip 添付付きで公開されていることを確認する。 +3. リリースノートに主要変更が過不足なく記載されていることを確認する。 + +## 失敗時対応 + +1. タグ名が `release-*` 形式か再確認する。 +2. 対象ブランチに `.github/workflows/release.yml` があるか確認する。 +3. 配布物サイズが異常な場合は `Prepare dist directory` の `rsync --exclude` を見直す。 +4. タグやり直しが必要な場合のみ、以下を順に実行する。 + +```bash +git tag -d release-x.y.z +git push --delete origin release-x.y.z +git tag release-x.y.z +git push origin release-x.y.z +``` diff --git a/.codex/skills/release-support/references/release-note-template.md b/.codex/skills/release-support/references/release-note-template.md new file mode 100644 index 000000000..22f3f9b9b --- /dev/null +++ b/.codex/skills/release-support/references/release-note-template.md @@ -0,0 +1,33 @@ +# Release Note Template + +以下を下書きに追記して最終版へ整える。一次情報の優先順位は「PR本文/差分 > コミット件名」。 + +## 1. タイトル + +- 形式例: `Evolution CMS JP Edition 1.2.1J リリース` + +## 2. 概要(3行以内) + +- 何が改善されたか +- 利用者への影響 +- アップグレード推奨有無 + +## 3. 主な変更 + +- `feat` / `fix` / `refactor` の順に重要項目を3-7件へ圧縮する。 +- 内部変更のみの場合は運用影響の有無を明示する。 + +## 4. Pull Request + +- スクリプト出力の PR 一覧から主要 PR を選び、1行コメントを足す。 + +## 5. アップグレード時の注意 + +- 互換性影響 +- 設定変更の有無 +- キャッシュクリア要否 + +## 6. 配布情報 + +- GitHub Release URL +- zip 資材名 diff --git a/.codex/skills/release-support/scripts/generate_release_draft.sh b/.codex/skills/release-support/scripts/generate_release_draft.sh new file mode 100755 index 000000000..958ead7f1 --- /dev/null +++ b/.codex/skills/release-support/scripts/generate_release_draft.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash +set -euo pipefail + +from_tag="" +to_ref="HEAD" +output_path="" +include_commit_subjects=0 + +usage() { + cat <<'USAGE' +Usage: + generate_release_draft.sh [--from ] [--to ] [--output ] [--include-commit-subjects] + +Options: + --from Base tag (default: latest reachable release-* tag) + --to Target ref (default: HEAD) + --output Output markdown file path (default: stdout) + --include-commit-subjects Append commit subjects as supplemental info + -h, --help +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --from) + from_tag="${2:-}" + shift 2 + ;; + --to) + to_ref="${2:-}" + shift 2 + ;; + --output) + output_path="${2:-}" + shift 2 + ;; + --include-commit-subjects) + include_commit_subjects=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if ! git rev-parse --verify "$to_ref" >/dev/null 2>&1; then + echo "Target ref not found: $to_ref" >&2 + exit 1 +fi + +if [[ -z "$from_tag" ]]; then + from_tag="$(git describe --tags --match 'release-*' --abbrev=0 "$to_ref" 2>/dev/null || true)" +fi + +if [[ -z "$from_tag" ]]; then + echo "Base release tag is required. Use --from ." >&2 + exit 1 +fi + +if ! git rev-parse --verify "$from_tag" >/dev/null 2>&1; then + echo "Base tag not found: $from_tag" >&2 + exit 1 +fi + +range="${from_tag}..${to_ref}" +remote_url="$(git config --get remote.origin.url || true)" +repo_url="" + +if [[ "$remote_url" =~ ^git@github.com:(.+)\.git$ ]]; then + repo_url="https://github.com/${BASH_REMATCH[1]}" +elif [[ "$remote_url" =~ ^https://github.com/(.+)\.git$ ]]; then + repo_url="https://github.com/${BASH_REMATCH[1]}" +elif [[ "$remote_url" =~ ^https://github.com/.+ ]]; then + repo_url="${remote_url%.git}" +fi + +compare_url="" +if [[ -n "$repo_url" ]]; then + compare_url="${repo_url}/compare/${from_tag}...${to_ref}" +fi + +tmp_prs="$(mktemp)" +tmp_dirs="$(mktemp)" +tmp_files="$(mktemp)" +tmp_files_all="$(mktemp)" +trap 'rm -f "$tmp_prs" "$tmp_dirs" "$tmp_files" "$tmp_files_all"' EXIT + +# Parse merge commits and prefer PR titles from merge body. +git log --merges --pretty='%s%x1f%b%x1e' "$range" | \ +awk -v RS='\x1e' -v FS='\x1f' ' + NF { + subject=$1 + body=$2 + pr="" + if (match(subject, /Merge pull request #([0-9]+)/, m)) { + pr=m[1] + } else { + next + } + + title="" + n=split(body, lines, /\n/) + for (i=1; i<=n; i++) { + line=lines[i] + gsub(/^[[:space:]]+|[[:space:]]+$/, "", line) + if (line != "") { + title=line + break + } + } + if (title == "") title=subject + print pr "\t" title + } +' > "$tmp_prs" + +# Directory-level summary from diff. +git diff --name-only "$range" > "$tmp_files_all" +grep -Ev '^(\.agent/|\.codex/|\.claude/)' "$tmp_files_all" > "$tmp_files" || true +if [[ ! -s "$tmp_files" ]]; then + cp "$tmp_files_all" "$tmp_files" +fi +awk -F/ ' + NF { + top=$1 + if ($0 ~ /^[^.\/]+$/) top="(root)" + count[top]++ + } + END { + for (k in count) print count[k] "\t" k + } +' "$tmp_files" | sort -rn > "$tmp_dirs" + +commit_count="$(git rev-list --count "$range")" +file_count="$(wc -l < "$tmp_files" | tr -d ' ')" +generated_date="$(date +%F)" + +{ + echo "# リリースノート(下書き)" + echo + echo "- 生成日: ${generated_date}" + echo "- 比較範囲: ${from_tag}..${to_ref}" + echo "- コミット数: ${commit_count}" + echo "- 変更ファイル数: ${file_count}" + if [[ -n "$compare_url" ]]; then + echo "- Compare: ${compare_url}" + fi + echo + + echo "## 概要" + echo "- [ここに今回リリースの要点を1-3行で記載]" + echo + + echo "## 変更サマリー(差分ベース)" + if [[ -s "$tmp_dirs" ]]; then + head -n 10 "$tmp_dirs" | while IFS=$'\t' read -r count dir; do + echo "- ${dir}: ${count} files" + done + else + echo "- 差分なし" + fi + echo + + echo "## 主な Pull Request" + if [[ -s "$tmp_prs" ]]; then + while IFS=$'\t' read -r pr title; do + if [[ -z "$pr" ]]; then + echo "- ${title}" + elif [[ -n "$repo_url" ]]; then + echo "- #${pr}: ${title} (${repo_url}/pull/${pr})" + else + echo "- #${pr}: ${title}" + fi + done < "$tmp_prs" + else + echo "- なし(マージPRが見つからないため、差分ベースで要約する)" + fi + echo + + echo "## 変更ファイル(先頭20)" + if [[ -s "$tmp_files" ]]; then + head -n 20 "$tmp_files" | sed 's/^/- /' + else + echo "- なし" + fi + echo + + if [[ "$include_commit_subjects" -eq 1 ]]; then + echo "## 参考: コミット件名" + git log --no-merges --pretty='- %s (%h)' "$range" + echo + fi + + echo "## 最終確認" + echo "- [ ] manager/includes/version.inc.php の版数と日付が正しい" + echo "- [ ] GitHub Actions Build Release Package が成功" + echo "- [ ] リリース資材(zip)の添付を確認" +} > "${output_path:-/dev/stdout}" diff --git a/.codex/skills/url-article-reader/SKILL.md b/.codex/skills/url-article-reader/SKILL.md new file mode 100644 index 000000000..527c78b23 --- /dev/null +++ b/.codex/skills/url-article-reader/SKILL.md @@ -0,0 +1,66 @@ +--- +name: url-article-reader +description: 指定されたURLの記事本文を取得し、事実ベースで要約・引用・論点整理を行うスキル。ユーザーが「このURLを読んで調査して」「必ずURLを参照して」と要求する場合、または不具合報告URLの一次情報を根拠として扱う場合に使う。 +--- + +# Url Article Reader + +一次情報(指定URL本文)を必須根拠として扱う。 + +## 実行ルール + +1. 最初に指定URLそのものを直接参照する。 +2. 検索結果要約で代替しない。本文取得前に結論を断定しない。 +3. 取得失敗時は試行経路と失敗理由を記録する。 +4. 本文未取得なら本文貼り付けを依頼する。 +5. 事実はURLと本文断片を対応付けて示す。 + +## Workflow + +### 1. URL直接取得 + +1. 指定URLをそのまま開く(クエリ文字列を改変しない)。 +2. 取得成功時は、タイトル・投稿日・投稿者・本文要点を抽出する。 +3. 取得失敗時は 2 へ進む。 + +### 2. 代替経路取得(本文確認のため) + +1. 同一URLのキャッシュ・ミラー・アーカイブを試す。 +2. URLの一部変更は最小限にする(`http/https` 差分、`www` 有無のみ)。 +3. 検索を使う場合も「元URL本文へ到達すること」だけを目的にする。 +4. 代替経路でも本文が得られなければ、取得不能として明示する。 + +### 3. 調査アウトプット + +1. URL本文で確認できた事実のみ列挙する。 +2. 背景は「発生条件 / 技術要因 / 環境要因」に分ける。 +3. 本文にない情報は未確定として明示する。 +4. 修正判断に必要な追加確認項目を短く示す。 + +## 返答テンプレート + +```text +参照URL: +取得ステータス: 成功 | 失敗 +取得経路: +- <経路名>: 成功/失敗(理由) + +本文要点: +- ... + +背景整理: +- 発生条件: +- 技術要因: +- 環境要因: + +未確定事項: +- ... + +追加確認: +1. ... +2. ... +``` + +## References + +- 取得可否判定と報告粒度は `references/retrieval-checklist.md` に従う。 diff --git a/.codex/skills/url-article-reader/agents/openai.yaml b/.codex/skills/url-article-reader/agents/openai.yaml new file mode 100644 index 000000000..7d7264625 --- /dev/null +++ b/.codex/skills/url-article-reader/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "URL Article Reader" + short_description: "指定URL記事の本文取得と根拠付き要約を標準化するスキル" + default_prompt: "指定URLを必ず直接参照し、本文を取得して根拠付きで要約してください。取得失敗時は代替経路と失敗理由を明示してください。" diff --git a/.codex/skills/url-article-reader/references/retrieval-checklist.md b/.codex/skills/url-article-reader/references/retrieval-checklist.md new file mode 100644 index 000000000..2c272ef4f --- /dev/null +++ b/.codex/skills/url-article-reader/references/retrieval-checklist.md @@ -0,0 +1,21 @@ +# Retrieval Checklist + +## 必須チェック + +1. 指定URLを改変せずに直接参照した。 +2. 本文から最低3つの事実を抽出した。 +3. 各事実に根拠箇所(短い本文断片)を対応付けた。 +4. 取得不能時は試行経路と失敗理由を列挙した。 +5. 本文未取得のまま原因を断定していない。 + +## 失敗理由の例 + +- DNS解決不可 +- タイムアウト +- 403/429等のアクセス制限 +- ログイン必須 +- スクレイピング対策で本文取得不可 + +## 完了条件 + +- 「参照URL / 取得ステータス / 取得経路 / 本文要点 / 未確定事項」が揃っている。 diff --git a/.github/codex-pr-rules.md b/.github/codex-pr-rules.md index 5699086ed..7056d6b62 100644 --- a/.github/codex-pr-rules.md +++ b/.github/codex-pr-rules.md @@ -3,10 +3,12 @@ When creating Pull Requests in this repository, follow these rules strictly. ## Pull Request creation + - Do not push directly to the main branch - Always create a Pull Request ## PR title rules + - PR titles must be suitable for GitHub release notes - Write PR titles in Japanese - Titles must clearly describe the change @@ -21,11 +23,13 @@ When creating Pull Requests in this repository, follow these rules strictly. - IE 向けレガシーコードを削除 ## PR description rules + - Include a Summary section (1–2 sentences) - Include a Notes section only if there are compatibility concerns - Avoid long explanations ## Labeling rules + - Replace or add one of the following labels based on the change intent: - enhancement - bug diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index adf53f0a2..66303634f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,13 +19,20 @@ jobs: rsync -a ./ dist/ \ --exclude='.git/' \ --exclude='.github/' \ + --exclude='.agent/' \ + --exclude='.claude/' \ + --exclude='.codex/' \ --exclude='.gitignore' \ --exclude='.gitkeep' \ --exclude='.gitattributes' \ --exclude='.editorconfig' \ + --exclude='.env' \ + --exclude='.env.*' \ --exclude='dist/' \ --exclude='docs/' \ --exclude='**/docs/' \ + --exclude='evo' \ + --exclude='manager/includes/cli/' \ --exclude='readme*' \ --exclude='README*' \ --exclude='AGENTS.md' diff --git a/AGENTS.md b/AGENTS.md index c8f2266e9..30ac4c517 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,12 @@ # 開発ガイドライン +このドキュメントは、このリポジトリにおけるAI駆動実装のローカル判断基準として最上位に扱う。 + +AIは実装前に必ずこのドキュメントを参照し、理解した上で「AGENTS.mdを読みました」と宣言して作業を進める。 + ## 基本原則 -SOLID / KISS / YAGNI / DRY / PIE(自己検証可能な実装) / SSOT(真実の単一情報源)を遵守する。 +SOLID / KISS / YAGNI / DRY / PIE / SSOT を遵守する。 --- @@ -16,74 +20,22 @@ SOLID / KISS / YAGNI / DRY / PIE(自己検証可能な実装) / SSOT(真 --- -## 補助ルール - -* **既存パターンの評価と改善**: 新機能追加時は既存パターンを必ず調査する。ただし不適切・冗長・設計的に弱い場合は改善を優先する。局所的な新方式導入は禁止し、改善は横断的に適用する前提で設計する。 -* **ヘルパー利用**: `manager/includes/helpers.php` の `evo()` / `db()` / `manager()` を経由してグローバルオブジェクトへアクセスする。 -* **スーパーグローバル変数の禁止**: - - * `$_GET` → `getv($key, $default)` / `$_POST` → `postv($key, $default)` / `$_REQUEST` → `anyv($key, $default)` - * `$_SERVER` → `serverv($key, $default)` / `$_COOKIE` → `cookiev($key, $default)` - * `$_SESSION` → `sessionv($key, $default)`(読み取り) / `sessionv('*key', $value)`(書き込み) - * リクエストメソッド判定 → `is_post()` / `is_get()` -* **DB操作のセキュリティ**: エスケープはDB操作直前で行う。 - - * `db()->insert(db()->escape($data), $table)` / `db()->update(db()->escape($data), $table, $where)` - * ❌ 変数宣言時の個別エスケープ / ❌ `compact()` 関数 -* **コーディングスタイル**: - - * 配列は `[]`(`array()` 非推奨)/ デフォルト値は `?:` / 早期リターン - * 文字列補間 `"{$var}"` / インデント: スペース4つ / `?>` 不要 -* **ログ**: `evo()->logEvent()` を使用 -* **レビュー**: 日本語 -* **コミットメッセージ**: 日本語で生成(Conventional Commits 準拠) - ---- - -## Conventional Commits - -### フォーマット +## 重要方針 -``` -(optional scope): -``` - -* type は英語固定 -* subject は日本語・簡潔に・現在形・句点なし - -### type 一覧 - -| type | 用途 | -| -------- | ------ | -| feat | 新機能 | -| fix | 不具合修正 | -| refactor | 内部改善 | -| perf | 性能改善 | -| docs | ドキュメント | -| style | 形式修正 | -| test | テスト | -| chore | 雑務 | -| ci | CI変更 | - -### 例 - -``` -feat(parser): キャッシュ生成前にフックを追加 -``` - -``` -fix(db): 実行直前でエスケープ処理を統一 -``` +* 実装は既存パターンを調査して整合させる。ただし不適切な既存実装は改善を優先する。 +* 局所的な独自方式の導入は禁止し、横断適用可能な設計で改善する。 +* セキュリティ(特にDBエスケープ)は実行直前で担保する。 +* キャッシュ整合性、イベント/フック整合性を破壊しない。 +* レビュー・コミュニケーションは日本語を基本とする。 --- -## AIが誤りやすいポイント +## 禁止事項(要点) -* 直接 `$modx` やスーパーグローバルへ触れる -* エスケープを代入時に行う -* 既存フック位置を無視して新イベントを追加する -* キャッシュ無効化条件を考慮しない -* 小規模修正で新アーキテクチャを導入する +* `$modx` やスーパーグローバルへの直接アクセス +* 変数代入時の早期エスケープ +* フック位置・キャッシュ無効化条件の無視 +* 小規模修正への新アーキテクチャ持ち込み --- @@ -106,19 +58,14 @@ fix(db): 実行直前でエスケープ処理を統一 --- -## DocumentParser - -中心: `manager/includes/document.parser.class.inc.php` -実装時は `executeParser()` / `prepareResponse()` / `parseDocumentSource()` / `postProcess()` のどこに影響するかを必ず明示する。 - ---- - ## ドキュメントマップ `assets/docs/` 配下を必ず参照する。 | ドキュメント | 主題 | | ----------------------- | ------ | +| `development-standards.md` | 実装規約(ヘルパー・入力値取得・DB・スタイル・ログ) | +| `commit-message-guidelines.md` | コミットメッセージ規約 | | `architecture.md` | 処理フロー | | `template-system.md` | タグ解析 | | `events-and-plugins.md` | イベント | @@ -129,12 +76,12 @@ fix(db): 実行直前でエスケープ処理を統一 --- -## 推奨ワークフロー +## 実装時の必須確認 -1. 既存実装を grep 検索して確認 -2. `architecture.md` で影響範囲整理 -3. キャッシュ影響を確認 -4. ヘルパー経由で実装 +* 既存実装を `rg` で調査し、重複実装を作らない。 +* `architecture.md` で影響範囲を整理する。 +* `cache-mechanism.md` と `events-and-plugins.md` で整合性を確認する。 +* DocumentParser関連は `manager/includes/document.parser.class.inc.php` のどの段階に影響するかを明示する。 --- @@ -150,7 +97,7 @@ fix(db): 実行直前でエスケープ処理を統一 ## ドキュメント運用 -* AGENTS.md は **判断基準のみを記載(詳細は docs へ分離)** +* AGENTS.md は **判断基準とポインタのみ** を記載する。 * ExecPlan は `.agent/plans/` に格納 * ロードマップは `.agent/roadmap.md` @@ -158,4 +105,4 @@ fix(db): 実行直前でエスケープ処理を統一 ## 肥大化防止ポリシー -AGENTS.md は「原則と判断基準のみ」を記載し、手順や詳細仕様は必ず `assets/docs/` に分離する。 +AGENTS.md は「原則・優先順位・禁止事項・参照先」のみを保持し、手順や詳細仕様は必ず `assets/docs/` に分離する。 diff --git a/assets/docs/commit-message-guidelines.md b/assets/docs/commit-message-guidelines.md new file mode 100644 index 000000000..6b73e20f3 --- /dev/null +++ b/assets/docs/commit-message-guidelines.md @@ -0,0 +1,36 @@ +# Commit Message Guidelines + +このドキュメントはコミットメッセージ生成の詳細規約を定義する。 + +## フォーマット + +```txt +(optional scope): +``` + +## 必須ルール + +* Conventional Commits 準拠 +* `type` は英語固定 +* `subject` は日本語、簡潔、現在形、句点なし + +## type 一覧 + +| type | 用途 | +| -------- | ---- | +| feat | 新機能 | +| fix | 不具合修正 | +| refactor | 内部改善 | +| perf | 性能改善 | +| docs | ドキュメント | +| style | 形式修正 | +| test | テスト | +| chore | 雑務 | +| ci | CI変更 | + +## 例 + +```txt +feat(parser): キャッシュ生成前にフックを追加 +fix(db): 実行直前でエスケープ処理を統一 +``` diff --git a/assets/docs/development-standards.md b/assets/docs/development-standards.md new file mode 100644 index 000000000..1fb8a927c --- /dev/null +++ b/assets/docs/development-standards.md @@ -0,0 +1,50 @@ +# Development Standards + +このドキュメントは実装時の具体規約を定義する。判断の優先順位は `AGENTS.md` に従う。 + +## ヘルパー利用 + +グローバルオブジェクトには `manager/includes/helpers.php` のヘルパー経由でアクセスする。 + +* `evo()` +* `db()` +* `manager()` + +## 入力値取得 + +スーパーグローバルは直接参照しない。 + +* `$_GET` -> `getv($key, $default)` +* `$_POST` -> `postv($key, $default)` +* `$_REQUEST` -> `anyv($key, $default)` +* `$_SERVER` -> `serverv($key, $default)` +* `$_COOKIE` -> `cookiev($key, $default)` +* `$_SESSION` 読み取り -> `sessionv($key, $default)` +* `$_SESSION` 書き込み -> `sessionv('*key', $value)` +* リクエストメソッド判定 -> `is_post()` / `is_get()` + +## DBセキュリティ + +エスケープはDB操作の直前で実行する。 + +* `db()->insert(db()->escape($data), $table)` +* `db()->update(db()->escape($data), $table, $where)` +* NG: 変数宣言時の個別エスケープ +* NG: `compact()` の使用 + +## コーディングスタイル + +* 配列は `[]` を使用する(`array()` 非推奨) +* デフォルト値は `?:` を優先する +* 早期リターンを優先する +* 文字列補間は `"{$var}"` を使用する +* インデントはスペース4つ +* PHP終端タグ `?>` は不要 + +## ログ + +ログは `evo()->logEvent()` を使用する。 + +## レビュー言語 + +レビューは日本語で実施する。 diff --git a/assets/docs/release-process.md b/assets/docs/release-process.md index 10c9aaabb..579902fe0 100644 --- a/assets/docs/release-process.md +++ b/assets/docs/release-process.md @@ -1,247 +1,76 @@ # リリース手順 -Evolution CMS JP Edition のリリースパッケージを作成し、GitHub Release として公開する手順。 +Evolution CMS JP Edition のリリースパッケージを GitHub Release として公開する最短手順。 + +## 基本方針 + +- パッケージ構成の SSOT は `.github/workflows/release.yml`。 +- このドキュメントには除外パターンを重複記載しない。 +- 除外追加・変更は `release.yml` の `Prepare dist directory` の `rsync --exclude` を編集する。 ## 基本手順 -### 1. リリースタグの作成 +1. タグを作成して push する(形式: `release-*`)。 ```bash -# タグ作成(例: release-1.5.0) git tag release-1.5.0 - -# タグを push git push origin release-1.5.0 ``` -### 2. GitHub Actions の自動実行 - -タグが push されると `.github/workflows/release.yml` が自動実行される。 - -**処理内容:** - -1. リポジトリをチェックアウト -2. `dist/` ディレクトリを作成 -3. 除外ファイルを除いてプロジェクトファイルを `dist/` にコピー -4. `evo-release-1.5.0.zip` を作成 -5. GitHub Release を自動作成し、zip ファイルを添付 - -### 3. リリース完了の確認 +2. GitHub Actions の `Build Release Package` が自動実行される。 +3. Releases 画面で zip 添付付きリリース作成を確認する。 -- GitHub の Releases ページで新しいリリースが作成されていることを確認 -- zip ファイルがダウンロード可能であることを確認 +## 除外設定の編集ルール -## 除外ファイル一覧 +- 変更箇所: `.github/workflows/release.yml` +- 対象ステップ: `Prepare dist directory` +- 記法: + - ファイルは `--exclude='filename'` + - ディレクトリは `--exclude='dirname/'`(末尾 `/` 必須) + - 全階層は `--exclude='**/dirname/'` +- 追加時は既存パターンと重複させない。 -リリースパッケージから除外されるファイル・ディレクトリ: +## ローカル事前確認 -``` -.git/ -.github/ -.gitignore -.gitkeep -.gitattributes -.editorconfig -dist/ -docs/ -**/docs/ -readme* -README* -AGENTS.md -``` - -### 除外設定の追加方法 - -除外ファイル・ディレクトリを追加する場合は [.github/workflows/release.yml](../../.github/workflows/release.yml) を編集する。 - -```yaml -- name: Prepare dist directory - run: | - mkdir dist - rsync -a ./ dist/ \ - --exclude='.git/' \ - --exclude='.github/' \ - --exclude='.gitignore' \ - --exclude='.gitkeep' \ - --exclude='.gitattributes' \ - --exclude='.editorconfig' \ - --exclude='dist/' \ - --exclude='docs/' \ - --exclude='**/docs/' \ - --exclude='readme*' \ - --exclude='README*' \ - --exclude='AGENTS.md' \ - --exclude='新しい除外パターン' # ← ここに追加 -``` +`release.yml` の `Prepare dist directory` と同じ `rsync` 行を使って確認する(コピペ元は必ず `release.yml`)。 -**パターンの書き方:** - -| パターン | 説明 | 例 | -|---------|------|-----| -| `filename` | ファイル名 | `--exclude='AGENTS.md'` | -| `dirname/` | ディレクトリ(末尾に `/`) | `--exclude='dist/'` | -| `**/dirname/` | すべての階層のディレクトリ | `--exclude='**/docs/'` | -| `*.ext` | 拡張子パターン | `--exclude='*.log'` | -| `prefix*` | プレフィックスパターン | `--exclude='readme*'` | -| `path/to/file` | 相対パス | `--exclude='temp/cache/'` | - -**追加例:** - -```yaml -# テストファイルを除外 ---exclude='**/test/' \ ---exclude='**/tests/' \ ---exclude='*.test.php' \ - -# 開発用ファイルを除外 ---exclude='.env' \ ---exclude='.env.local' \ ---exclude='composer.json' \ ---exclude='composer.lock' \ ---exclude='package.json' \ ---exclude='package-lock.json' \ - -# ログ・キャッシュを除外 ---exclude='*.log' \ ---exclude='temp/cache/*' \ ---exclude='temp/backup/*' +```bash +mkdir dist +# release.yml の rsync 行をそのまま実行 +ls -la dist/ +rm -rf dist/ ``` -**注意事項:** +## トラブル時 -- 各 `--exclude` の末尾にバックスラッシュ `\` を付けて改行する(最後の行を除く) -- パターンはシングルクォート `'...'` で囲む -- ディレクトリを除外する場合は末尾に `/` を付ける(例: `dist/`) -- `**/` は「すべての階層」を意味する(例: `**/docs/` は `assets/docs/` も `manager/media/docs/` も除外) +### ワークフローが動かない -## トラブルシューティング +- タグ名が `release-*` か確認する。 +- `.github/workflows/release.yml` が対象ブランチにあるか確認する。 -### タグの命名規則 +### zip が想定より大きい -- **必須形式**: `release-*`(例: `release-1.5.0`, `release-2.0.0-beta1`) -- 形式が異なるとワークフローが実行されない +- `release.yml` の除外パターン不足を確認する。 +- ローカルで `rsync` 事前確認を実施する。 -### GitHub Actions が失敗した場合 - -#### 1. ビルドログの確認 - -``` -GitHub リポジトリ → Actions タブ → 失敗したワークフロー → ログを確認 -``` - -#### 2. タグの削除と再作成 +### 失敗したタグをやり直す ```bash -# ローカルのタグを削除 git tag -d release-1.5.0 - -# リモートのタグを削除(GitHub Release も削除される) git push --delete origin release-1.5.0 - -# 修正後、再度タグを作成して push git tag release-1.5.0 git push origin release-1.5.0 ``` -#### 3. 手動でワークフローを再実行 - -GitHub の Actions タブから失敗したワークフローを開き、「Re-run jobs」をクリック。 - -### リリースパッケージの内容確認 - -ローカルでリリースパッケージの内容を事前確認する場合: - -```bash -# dist ディレクトリを作成 -mkdir dist - -# rsync で除外設定を適用してコピー -rsync -a ./ dist/ \ - --exclude='.git/' \ - --exclude='.github/' \ - --exclude='.gitignore' \ - --exclude='.gitkeep' \ - --exclude='.gitattributes' \ - --exclude='.editorconfig' \ - --exclude='dist/' \ - --exclude='docs/' \ - --exclude='**/docs/' \ - --exclude='readme*' \ - --exclude='README*' \ - --exclude='AGENTS.md' - -# 内容を確認 -ls -la dist/ - -# zip を作成(オプション) -cd dist -zip -r ../evo-test.zip . -cd .. - -# 確認後、dist ディレクトリを削除 -rm -rf dist/ evo-test.zip -``` - -### よくある問題 - -#### Q. タグを push したのにワークフローが実行されない - -**原因:** - -- タグ名が `release-*` 形式ではない -- `.github/workflows/release.yml` が該当ブランチに存在しない - -**対応:** - -```bash -# タグ名を確認 -git tag -l - -# ワークフローファイルの存在確認 -ls -la .github/workflows/release.yml -``` - -#### Q. zip ファイルが想定より大きい - -**原因:** - -- 除外設定が正しく適用されていない -- 不要なファイルがリポジトリに含まれている - -**対応:** - -1. ローカルで dist を作成して内容確認(上記手順参照) -2. 除外パターンを `.github/workflows/release.yml` に追加 - -#### Q. リリースノートを後から編集したい - -**対応:** - -GitHub の Releases ページから該当リリースを開き、「Edit release」で編集可能。 - -## バージョニング規則 - -- **メジャーリリース**: `release-2.0.0`(破壊的変更を含む) -- **マイナーリリース**: `release-1.5.0`(機能追加、下位互換性あり) -- **パッチリリース**: `release-1.4.1`(バグ修正のみ) -- **プレリリース**: `release-1.5.0-beta1`, `release-1.5.0-rc1` - -## リリース前チェックリスト - -- [ ] `CHANGELOG.md` の更新(該当バージョンの変更内容を記載) -- [ ] `manager/includes/version.inc.php` のバージョン番号更新 -- [ ] ローカルでの動作確認(インストール・アップグレード) -- [ ] テストケースの実行 -- [ ] ドキュメントの更新(`.agent/roadmap.md` など) -- [ ] リリースノートの準備(GitHub Release の説明文) - -## リリース後の対応 +## チェックリスト -1. リリース告知(フォーラム、SNS など) -2. 次期バージョンの開発ブランチ作成(必要に応じて) -3. `.agent/roadmap.md` の更新 +- [ ] `CHANGELOG.md` 更新 +- [ ] `manager/includes/version.inc.php` 更新 +- [ ] インストール/アップグレード動作確認 +- [ ] 必要テスト実行 +- [ ] リリースノート確認 -## 参考リンク +## 参考 -- GitHub Actions ワークフロー: [.github/workflows/release.yml](../../.github/workflows/release.yml) -- ロードマップ: [`../../.agent/roadmap.md`](../../.agent/roadmap.md) +- ワークフロー: `.github/workflows/release.yml` +- ロードマップ: `.agent/roadmap.md` diff --git a/install/functions.php b/install/functions.php index 8b235d6c3..8fca316bb 100644 --- a/install/functions.php +++ b/install/functions.php @@ -627,31 +627,6 @@ function isInstallerIpAllowed(array $config) return in_array(installerRemoteAddress(), $allowedIps, true); } -function installBasicAuthEnabled(array $config) -{ - $user = trim($config['basic_auth']['user'] ?? ''); - $password = $config['basic_auth']['password'] ?? ''; - - return $user !== '' && $password !== ''; -} - -function enforceInstallBasicAuth(array $config) -{ - if (!installBasicAuthEnabled($config)) { - return; - } - - $user = serverv('PHP_AUTH_USER', ''); - $password = serverv('PHP_AUTH_PW', ''); - if ($user === $config['basic_auth']['user'] && $password === $config['basic_auth']['password']) { - return; - } - - header('WWW-Authenticate: Basic realm="Evolution CMS Installer"'); - header('HTTP/1.0 401 Unauthorized'); - renderInstallConfigNotice('install_config_auth_message'); -} - function guardInstallerAccess() { $config = requireInstallConfig(); @@ -659,8 +634,6 @@ function guardInstallerAccess() if (!isInstallerIpAllowed($config)) { renderInstallConfigNotice('install_config_ip_message'); } - - enforceInstallBasicAuth($config); } function isManagerAuthenticated() diff --git a/install/install-config.sample.php b/install/install-config.sample.php index 894806ef1..3cebf8026 100644 --- a/install/install-config.sample.php +++ b/install/install-config.sample.php @@ -1,10 +1,5 @@ [ - 'user' => 'installer', - 'password' => 'change-me', - ], // IP addresses allowed to access the installer. Leave empty to allow access from any address. 'allowed_ips' => [ '127.0.0.1', diff --git a/install/instprocessor.php b/install/instprocessor.php index 37f07661f..d089a24c5 100644 --- a/install/instprocessor.php +++ b/install/instprocessor.php @@ -370,6 +370,8 @@ function register_install_error_handlers() log_install_event('Installation processor finished'); +removeInstallConfigFile(); + // setup completed! echo "

" . lang('installation_successful') . "

"; echo "

" . lang('to_log_into_content_manager') . "

"; @@ -385,6 +387,23 @@ function register_install_error_handlers() $_SESSION = []; +function removeInstallConfigFile() +{ + $configPath = installConfigPath(); + if (!is_file($configPath)) { + return; + } + + if (@unlink($configPath)) { + log_install_event('install-config.php removed automatically', ['path' => $configPath]); + echo '

' . lang('install_config_removed') . '

'; + return; + } + + log_install_event('Failed to remove install-config.php automatically', ['path' => $configPath]); + echo '

' . lang('install_config_remove_failed') . '

'; +} + function deleteCacheDirectory($cachePath) { if (!is_dir($cachePath)) { diff --git a/install/langs/english.inc.php b/install/langs/english.inc.php index d6807aea9..b4f45a9e6 100644 --- a/install/langs/english.inc.php +++ b/install/langs/english.inc.php @@ -201,11 +201,13 @@ $_lang["install_config_title"] = 'Installer access control'; $_lang["install_config_missing_message"] = 'Installation cannot continue because install-config.php is missing.'; $_lang["install_config_ip_message"] = 'Installation cannot continue because access from your IP address is not allowed. Please update allowed_ips in install-config.php.'; -$_lang["install_config_auth_message"] = 'Authentication is required to access the installer. Please enter the BASIC authentication credentials defined in install-config.php.'; +$_lang["install_config_auth_message"] = 'Please register your source IP address in allowed_ips within install-config.php.'; $_lang["install_config_step1"] = 'Copy %s to %s.'; -$_lang["install_config_step2"] = 'Edit install-config.php to set BASIC authentication and/or allowed IP addresses.'; +$_lang["install_config_step2"] = 'Edit install-config.php to set allowed_ips.'; $_lang["install_config_step3"] = 'Reload this page after saving the configuration file.'; $_lang["install_config_footer"] = 'Only administrators with server access should create this file to run the installer.'; +$_lang["install_config_removed"] = 'Removed install-config.php automatically.'; +$_lang["install_config_remove_failed"] = 'Could not remove install-config.php automatically. Please delete it manually after checking file permissions.'; $_lang["upgrade_auth_required_title"] = 'Manager authentication required for upgrade'; $_lang["upgrade_auth_required_message"] = '

To perform an upgrade, you must be logged into the Evolution CMS manager.

'; $_lang["upgrade_auth_login_link"] = 'Log in to manager'; diff --git a/install/langs/japanese-utf8.inc.php b/install/langs/japanese-utf8.inc.php index af8b75df2..4f9070b19 100644 --- a/install/langs/japanese-utf8.inc.php +++ b/install/langs/japanese-utf8.inc.php @@ -210,11 +210,13 @@ $_lang["install_config_title"] = 'インストーラーのアクセス制御'; $_lang["install_config_missing_message"] = 'install-config.php が存在しないため、インストールを続行できません。'; $_lang["install_config_ip_message"] = '現在の接続元IPアドレスからはインストールを実行できません。install-config.phpallowed_ips を更新してください。'; -$_lang["install_config_auth_message"] = 'インストーラーにアクセスするには BASIC 認証が必要です。install-config.php で設定したユーザー名とパスワードを入力してください。'; +$_lang["install_config_auth_message"] = 'install-config.phpallowed_ips に接続元IPアドレスを登録してください。'; $_lang["install_config_step1"] = '%s を %s にコピーしてください。'; -$_lang["install_config_step2"] = 'install-config.php を編集し、BASIC 認証のユーザー名とパスワード、または allowed_ips を設定してください。'; +$_lang["install_config_step2"] = 'install-config.php を編集し、allowed_ips を設定してください。'; $_lang["install_config_step3"] = '設定ファイルを保存したら、このページを再読み込みしてください。'; $_lang["install_config_footer"] = 'インストールを実行できるのはサーバーの操作権限を持つ管理者のみとしてください。'; +$_lang["install_config_removed"] = 'install-config.php を自動削除しました。'; +$_lang["install_config_remove_failed"] = 'install-config.php を自動削除できませんでした。権限を確認して手動で削除してください。'; $_lang["upgrade_auth_required_title"] = 'アップグレードには管理者認証が必要です'; $_lang["upgrade_auth_required_message"] = '

アップグレードを実行するには、Evolution CMS の管理画面にログインしている必要があります。

'; $_lang["upgrade_auth_login_link"] = '管理画面にログイン'; diff --git a/manager/actions/tool/mutate_settings.dynamic.php b/manager/actions/tool/mutate_settings.dynamic.php index feeecbc12..d1db0ff15 100644 --- a/manager/actions/tool/mutate_settings.dynamic.php +++ b/manager/actions/tool/mutate_settings.dynamic.php @@ -30,7 +30,7 @@ $settings = settings(); extract($settings, EXTR_OVERWRITE); -// load languages and keys +// load language names $lang_keys = []; $dir = scandir(MODX_CORE_PATH . 'lang'); foreach ($dir as $filename) { @@ -38,7 +38,7 @@ continue; } $languagename = str_replace('.inc.php', '', $filename); - $lang_keys[$languagename] = get_lang_keys($filename); + $lang_keys[$languagename] = true; } ?>