From 853f93134abc1dbdc439174096850c35f79c7dec Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sun, 11 Oct 2026 05:15:23 +0000 Subject: [PATCH 1/3] feat: add ARC-like contests (diverta2019-2, nikkei2019-2-qual, ddcc2020-qual) and tasks Co-Authored-By: Claude Sonnet 5.5 --- .../2026-10-11/add-arc-like-contests/plan.md | 76 +++++++++++ prisma/tasks.ts | 126 ++++++++++++++++++ src/lib/contests/fixtures/contest_type.ts | 12 ++ src/lib/contests/utils/prefixes.ts | 6 +- 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md diff --git a/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md b/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md new file mode 100644 index 000000000..8711782f8 --- /dev/null +++ b/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md @@ -0,0 +1,76 @@ +# ARC-Like コンテスト 3 件の追加(#4140) + +## 概要 + +[#4140](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/4140) で、以下 3 コンテストの問題を追加する。 +いずれも ARC 相当(`ContestType.ARC_LIKE`)として分類する。 + +- `nikkei2019-2-qual`(第二回全国統一プログラミング王決定戦予選) +- `ddcc2020-qual`(DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選) +- `diverta2019-2`(diverta 2019 Programming Contest 2) + +参考 PR:[#3951](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/pull/3951)(コンテスト名マッピング + `prisma/tasks.ts` + テストケースの追加)。 +#3951 以降、コンテスト関連のユーティリティは `src/lib/utils/contest.ts` から `src/lib/contests/utils/` へ、テストケースは `src/lib/contests/fixtures/` へ移動している。 + +## 決定事項 + +| 項目 | 決定 | +| ------------------- | ------------------------------------------------------- | +| `prisma/tasks.ts` | 各コンテストの全問題を追加 | +| `ARC_LIKE` の表示名 | 公式の日本語名(既存の `nikkei2019-qual` と同じ書き方) | +| 本戦(`*-final`) | スコープ外(YAGNI) | + +## 設計の根拠 + +- `ARC_LIKE` は `arcLikePrefixes.has(id)` による完全一致で判定する([classification.ts](../../../../src/lib/contests/utils/classification.ts))。既存の `diverta2019` キーでは `diverta2019-2` にマッチしないため、3 件ともキーを追加する必要がある。 +- `ARC_LIKE` にはラベル生成関数がないため、表示ラベルはコンテスト ID の大文字化(例:`DDCC2020-QUAL`)になる。既存の ARC-Like と同じ扱いなので、ラベル生成側は変更しない。 +- 分類ルールは先にマッチしたものが優先される。新しい ID が `ARC_LIKE` より前のルール(`abc\d{3}`、`past`、`joi` などのプレフィックス)にマッチしないことを確認済み。 + +### 採用しなかった案 + +- `ARC_LIKE` をプレフィックス一致に変更する:`diverta2019` などのキーで想定外の ID まで拾ってしまうため不採用。完全一致の方針を維持する。 +- ARC-Like 用のラベル生成関数を追加する:本件の要件外で、既存の ARC-Like コンテストの表示も変わってしまうため不採用。 + +## Phase 1:分類(テスト → 実装) + +レイヤー:Utilities(`src/lib/contests/utils/`)+ テストデータ(`src/lib/contests/fixtures/`)。 + +1. [contest_type.ts](../../../../src/lib/contests/fixtures/contest_type.ts) の `arcLike` に 3 件を追加する(`nikkei2019-qual` の後、`acl1` の前)。 + ```typescript + createTestCaseForContestType('NIKKEI2019-2 QUAL')({ + contestId: 'nikkei2019-2-qual', + expected: ContestType.ARC_LIKE, + }), + createTestCaseForContestType('DDCC2020 QUAL')({ + contestId: 'ddcc2020-qual', + expected: ContestType.ARC_LIKE, + }), + createTestCaseForContestType('diverta 2019 Programming Contest 2')({ + contestId: 'diverta2019-2', + expected: ContestType.ARC_LIKE, + }), + ``` +2. `pnpm test:unit src/lib/contests/utils/classification.test.ts` を実行し、失敗すること(Red)を確認する。 +3. [prefixes.ts](../../../../src/lib/contests/utils/prefixes.ts) の `ARC_LIKE` に以下を追加する(開催順に並べる)。 + ```typescript + 'diverta2019-2': 'diverta 2019 Programming Contest 2', + 'nikkei2019-2-qual': '第二回全国統一プログラミング王決定戦予選', + 'ddcc2020-qual': 'DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選', + ``` +4. テストが通ること(Green)を確認する。 + +## Phase 2:問題データ(`prisma/tasks.ts`) + +レイヤー:DB シード。分岐を含まないデータ追加なので、テストファーストは省略する。 + +- ARC-Like ブロックの `acl1` の後、`tenka1-2019` の前に、開催日の新しい順で追加する:`ddcc2020-qual` → `nikkei2019-2-qual` → `diverta2019-2`。 +- 各ブロックの中は既存と同じく問題番号の降順(F → A)で並べる。 +- `id` / `problem_index` / `name` / `title` は [problems.json](https://kenkoooo.com/atcoder/resources/problems.json) の値をそのまま使う。 + - `ddcc2020-qual`:A〜F(ユーザー提供の値で確定済み) + - `diverta2019-2`:A〜F(ユーザー提供の値で確定済み) + - `nikkei2019-2-qual`:A〜F(ユーザー提供の値で確定済み) + +## Phase 3:検証 + +- `pnpm format`、`pnpm lint`、`pnpm check`、`pnpm test:unit`、`git diff --check` を実行する。 +- クロスレビュー:対象外(手で編集するファイルは 3 件で、認証・スキーマ・公開インターフェースの変更もない)。 diff --git a/prisma/tasks.ts b/prisma/tasks.ts index 657357df0..25279adb2 100755 --- a/prisma/tasks.ts +++ b/prisma/tasks.ts @@ -8256,6 +8256,132 @@ export const tasks = [ name: 'Reachable Towns', title: 'A. Reachable Towns', }, + { + id: 'ddcc2020_qual_f', + contest_id: 'ddcc2020-qual', + problem_index: 'F', + name: 'DISCOSMOS', + title: 'F. DISCOSMOS', + }, + { + id: 'ddcc2020_qual_e', + contest_id: 'ddcc2020-qual', + problem_index: 'E', + name: 'Majority of Balls', + title: 'E. Majority of Balls', + }, + { + id: 'ddcc2020_qual_d', + contest_id: 'ddcc2020-qual', + problem_index: 'D', + name: 'Digit Sum Replace', + title: 'D. Digit Sum Replace', + }, + { + id: 'ddcc2020_qual_c', + contest_id: 'ddcc2020-qual', + problem_index: 'C', + name: 'Strawberry Cakes', + title: 'C. Strawberry Cakes', + }, + { + id: 'ddcc2020_qual_b', + contest_id: 'ddcc2020-qual', + problem_index: 'B', + name: 'Iron Bar Cutting', + title: 'B. Iron Bar Cutting', + }, + { + id: 'ddcc2020_qual_a', + contest_id: 'ddcc2020-qual', + problem_index: 'A', + name: 'DDCC Finals', + title: 'A. DDCC Finals', + }, + { + id: 'nikkei2019_2_qual_f', + contest_id: 'nikkei2019-2-qual', + problem_index: 'F', + name: 'Mirror Frame', + title: 'F. Mirror Frame', + }, + { + id: 'nikkei2019_2_qual_e', + contest_id: 'nikkei2019-2-qual', + problem_index: 'E', + name: 'Non-triangular Triplets', + title: 'E. Non-triangular Triplets', + }, + { + id: 'nikkei2019_2_qual_d', + contest_id: 'nikkei2019-2-qual', + problem_index: 'D', + name: 'Shortest Path on a Line', + title: 'D. Shortest Path on a Line', + }, + { + id: 'nikkei2019_2_qual_c', + contest_id: 'nikkei2019-2-qual', + problem_index: 'C', + name: 'Swaps', + title: 'C. Swaps', + }, + { + id: 'nikkei2019_2_qual_b', + contest_id: 'nikkei2019-2-qual', + problem_index: 'B', + name: 'Counting of Trees', + title: 'B. Counting of Trees', + }, + { + id: 'nikkei2019_2_qual_a', + contest_id: 'nikkei2019-2-qual', + problem_index: 'A', + name: 'Sum of Two Integers', + title: 'A. Sum of Two Integers', + }, + { + id: 'diverta2019_2_f', + contest_id: 'diverta2019-2', + problem_index: 'F', + name: 'Diverta City', + title: 'F. Diverta City', + }, + { + id: 'diverta2019_2_e', + contest_id: 'diverta2019-2', + problem_index: 'E', + name: 'Balanced Piles', + title: 'E. Balanced Piles', + }, + { + id: 'diverta2019_2_d', + contest_id: 'diverta2019-2', + problem_index: 'D', + name: 'Squirrel Merchant', + title: 'D. Squirrel Merchant', + }, + { + id: 'diverta2019_2_c', + contest_id: 'diverta2019-2', + problem_index: 'C', + name: 'Successive Subtraction', + title: 'C. Successive Subtraction', + }, + { + id: 'diverta2019_2_b', + contest_id: 'diverta2019-2', + problem_index: 'B', + name: 'Picking Up', + title: 'B. Picking Up', + }, + { + id: 'diverta2019_2_a', + contest_id: 'diverta2019-2', + problem_index: 'A', + name: 'Ball Distribution', + title: 'A. Ball Distribution', + }, { id: 'tenka1_2019_f', contest_id: 'tenka1-2019', diff --git a/src/lib/contests/fixtures/contest_type.ts b/src/lib/contests/fixtures/contest_type.ts index 32663ea60..c8b3670a0 100644 --- a/src/lib/contests/fixtures/contest_type.ts +++ b/src/lib/contests/fixtures/contest_type.ts @@ -300,6 +300,10 @@ export const arcLike = [ contestId: 'diverta2019', expected: ContestType.ARC_LIKE, }), + createTestCaseForContestType('diverta 2019 Programming Contest 2')({ + contestId: 'diverta2019-2', + expected: ContestType.ARC_LIKE, + }), createTestCaseForContestType('KEYENCE2019')({ contestId: 'keyence2019', expected: ContestType.ARC_LIKE, @@ -320,6 +324,14 @@ export const arcLike = [ contestId: 'nikkei2019-qual', expected: ContestType.ARC_LIKE, }), + createTestCaseForContestType('NIKKEI2019-2 QUAL')({ + contestId: 'nikkei2019-2-qual', + expected: ContestType.ARC_LIKE, + }), + createTestCaseForContestType('DDCC2020 QUAL')({ + contestId: 'ddcc2020-qual', + expected: ContestType.ARC_LIKE, + }), createTestCaseForContestType('ACL1')({ contestId: 'acl1', expected: ContestType.ARC_LIKE, diff --git a/src/lib/contests/utils/prefixes.ts b/src/lib/contests/utils/prefixes.ts index 2956400e0..e3a94ce86 100644 --- a/src/lib/contests/utils/prefixes.ts +++ b/src/lib/contests/utils/prefixes.ts @@ -3,8 +3,9 @@ import type { ContestPrefix } from '$lib/contests/types/contest'; export const regexForJag = /^JAG(Prelim|Regional|Summer|Winter|Spring)\d{4}(-day\d+)?[A-Z]?$/; export const regexForAojUniversity = /^AOJ-[A-Z]+PC\d{4}/; -// HACK: As of December 2025, the following contests are applicable. +// HACK: As of October 2026, the following contests are applicable. // Note: The classification logic may need to be revised when new contests are added. +// ABC_LIKE / ARC_LIKE: exact match — add sequels (e.g. diverta2019-2) explicitly. Others: prefix match. export const ABC_LIKE: ContestPrefix = { 'tenka1-2017-beginner': 'Tenka1 Programmer Beginner Contest 2017', abl: 'ACL Beginner Contest', @@ -31,11 +32,14 @@ export const ARC_LIKE: ContestPrefix = { 'dwacon5th-prelims': '第5回 ドワンゴからの挑戦状 予選', 'dwacon6th-prelims': '第6回 ドワンゴからの挑戦状 予選', diverta2019: 'diverta 2019 Programming Contest', + 'diverta2019-2': 'diverta 2019 Programming Contest 2', keyence2019: 'キーエンス プログラミング コンテスト 2019', keyence2020: 'キーエンス プログラミング コンテスト 2020', keyence2021: 'キーエンス プログラミング コンテスト 2021', 'jsc2019-qual': '第一回日本最強プログラマー学生選手権-予選-', 'nikkei2019-qual': '全国統一プログラミング王決定戦予選', + 'nikkei2019-2-qual': '第二回全国統一プログラミング王決定戦予選', + 'ddcc2020-qual': 'DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選', acl1: 'ACL Contest 1', } as const; From b58cc4050239266cdf6c430cb8af8c2736fc8fd7 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sun, 11 Oct 2026 05:17:33 +0000 Subject: [PATCH 2/3] docs: simplify add-arc-like-contests plan Co-Authored-By: Claude Sonnet 5.5 --- .../2026-10-11/add-arc-like-contests/plan.md | 84 ++++++------------- 1 file changed, 24 insertions(+), 60 deletions(-) diff --git a/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md b/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md index 8711782f8..564e53f47 100644 --- a/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md +++ b/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md @@ -2,75 +2,39 @@ ## 概要 -[#4140](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/4140) で、以下 3 コンテストの問題を追加する。 -いずれも ARC 相当(`ContestType.ARC_LIKE`)として分類する。 - -- `nikkei2019-2-qual`(第二回全国統一プログラミング王決定戦予選) -- `ddcc2020-qual`(DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選) -- `diverta2019-2`(diverta 2019 Programming Contest 2) - -参考 PR:[#3951](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/pull/3951)(コンテスト名マッピング + `prisma/tasks.ts` + テストケースの追加)。 -#3951 以降、コンテスト関連のユーティリティは `src/lib/utils/contest.ts` から `src/lib/contests/utils/` へ、テストケースは `src/lib/contests/fixtures/` へ移動している。 +[#4140](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/4140) で、`nikkei2019-2-qual`・`ddcc2020-qual`・`diverta2019-2` の全問題を `prisma/tasks.ts` に追加し、`ContestType.ARC_LIKE` に分類した。 +3 件とも ARC 相当とするのはユーザーの判断による(コンテスト ID や問題構成からは機械的に決まらない)。 ## 決定事項 -| 項目 | 決定 | -| ------------------- | ------------------------------------------------------- | -| `prisma/tasks.ts` | 各コンテストの全問題を追加 | -| `ARC_LIKE` の表示名 | 公式の日本語名(既存の `nikkei2019-qual` と同じ書き方) | -| 本戦(`*-final`) | スコープ外(YAGNI) | - -## 設計の根拠 - -- `ARC_LIKE` は `arcLikePrefixes.has(id)` による完全一致で判定する([classification.ts](../../../../src/lib/contests/utils/classification.ts))。既存の `diverta2019` キーでは `diverta2019-2` にマッチしないため、3 件ともキーを追加する必要がある。 -- `ARC_LIKE` にはラベル生成関数がないため、表示ラベルはコンテスト ID の大文字化(例:`DDCC2020-QUAL`)になる。既存の ARC-Like と同じ扱いなので、ラベル生成側は変更しない。 -- 分類ルールは先にマッチしたものが優先される。新しい ID が `ARC_LIKE` より前のルール(`abc\d{3}`、`past`、`joi` などのプレフィックス)にマッチしないことを確認済み。 - -### 採用しなかった案 +| 項目 | 決定 | 理由 | +| ------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ | +| 追加する問題 | 各コンテストの全問題(A〜F) | `tenka1-2019` のように一部だけ載せる前例もあるが、本件は全問題を対象とした | +| `ARC_LIKE` のコンテスト名 | 公式名(日本語名があれば日本語) | 既存の `nikkei2019-qual` に合わせた。名前は AtCoder のコンテストページでユーザーが確認済み | +| 本戦(`*-final`) | スコープ外 | Issue の対象外(YAGNI) | +| 表示ラベル | 変更しない(コンテスト ID の大文字化のまま) | `ARC_LIKE` にはラベル生成関数がなく、既存の ARC-Like と同じ扱いにそろえる | -- `ARC_LIKE` をプレフィックス一致に変更する:`diverta2019` などのキーで想定外の ID まで拾ってしまうため不採用。完全一致の方針を維持する。 -- ARC-Like 用のラベル生成関数を追加する:本件の要件外で、既存の ARC-Like コンテストの表示も変わってしまうため不採用。 +## 前提として判明したこと -## Phase 1:分類(テスト → 実装) +- `ABC_LIKE` / `ARC_LIKE` は完全一致、その他のグループは前方一致で判定する。そのため、既存の `diverta2019` キーでは `diverta2019-2` を拾えず、続編にも個別のキーが要る。変数名(`ContestPrefix`、`arcLikePrefixes`)からは前方一致に見えるので、`prefixes.ts` にコメントを追加した。 +- 問題データの取得元は [problems.json](https://kenkoooo.com/atcoder/resources/problems.json) だが、エージェントからは atcoder.jp に到達できず、WebFetch は problems.json を途中で切り捨てる。今回は該当行をユーザーに貼ってもらった。 +- 参考にした #3951 以降、コンテスト関連のユーティリティは `src/lib/contests/utils/`、テストケースは `src/lib/contests/fixtures/` へ移動している。 -レイヤー:Utilities(`src/lib/contests/utils/`)+ テストデータ(`src/lib/contests/fixtures/`)。 +## 採用しなかった案 -1. [contest_type.ts](../../../../src/lib/contests/fixtures/contest_type.ts) の `arcLike` に 3 件を追加する(`nikkei2019-qual` の後、`acl1` の前)。 - ```typescript - createTestCaseForContestType('NIKKEI2019-2 QUAL')({ - contestId: 'nikkei2019-2-qual', - expected: ContestType.ARC_LIKE, - }), - createTestCaseForContestType('DDCC2020 QUAL')({ - contestId: 'ddcc2020-qual', - expected: ContestType.ARC_LIKE, - }), - createTestCaseForContestType('diverta 2019 Programming Contest 2')({ - contestId: 'diverta2019-2', - expected: ContestType.ARC_LIKE, - }), - ``` -2. `pnpm test:unit src/lib/contests/utils/classification.test.ts` を実行し、失敗すること(Red)を確認する。 -3. [prefixes.ts](../../../../src/lib/contests/utils/prefixes.ts) の `ARC_LIKE` に以下を追加する(開催順に並べる)。 - ```typescript - 'diverta2019-2': 'diverta 2019 Programming Contest 2', - 'nikkei2019-2-qual': '第二回全国統一プログラミング王決定戦予選', - 'ddcc2020-qual': 'DISCO presents ディスカバリーチャンネル コードコンテスト2020 予選', - ``` -4. テストが通ること(Green)を確認する。 +- `ARC_LIKE` を前方一致に変更する:`diverta2019` などのキーが想定外の ID まで拾ってしまう。 +- ARC-Like 用のラベル生成関数を追加する:要件外で、既存の ARC-Like コンテストの表示まで変わる。 -## Phase 2:問題データ(`prisma/tasks.ts`) +## 派生した変更 -レイヤー:DB シード。分岐を含まないデータ追加なので、テストファーストは省略する。 +同種の Issue(「問題を追加しましょう」)は繰り返し起きるが、既存の `add-contest-table-provider` skill は ContestType や Provider を新しく追加するときにしか読み込まれない。そこで以下を整備した。 -- ARC-Like ブロックの `acl1` の後、`tenka1-2019` の前に、開催日の新しい順で追加する:`ddcc2020-qual` → `nikkei2019-2-qual` → `diverta2019-2`。 -- 各ブロックの中は既存と同じく問題番号の降順(F → A)で並べる。 -- `id` / `problem_index` / `name` / `title` は [problems.json](https://kenkoooo.com/atcoder/resources/problems.json) の値をそのまま使う。 - - `ddcc2020-qual`:A〜F(ユーザー提供の値で確定済み) - - `diverta2019-2`:A〜F(ユーザー提供の値で確定済み) - - `nikkei2019-2-qual`:A〜F(ユーザー提供の値で確定済み) +- `add-tasks` skill を新設した(`SKILL.md` に概略、`instructions.md` に詳細)。AOJ 側の手順は #3951 の差分から読み取った下書きで、未検証。必要になったときに直す。 +- `add-contest-table-provider` の手順 0 と Layer 3 から `add-tasks` を参照するようにし、データ取得と照合方法の説明を一か所にまとめた。 +- `how-to-add-contest-table-provider.md` の「事前確認」に、完全一致と前方一致の違いを 1 行追記した。 +- Claude は `.claude/skills/` のシンボリックリンクから、Codex は AGENTS.md の skill 一覧から参照する。 -## Phase 3:検証 +## 検証 -- `pnpm format`、`pnpm lint`、`pnpm check`、`pnpm test:unit`、`git diff --check` を実行する。 -- クロスレビュー:対象外(手で編集するファイルは 3 件で、認証・スキーマ・公開インターフェースの変更もない)。 +- format、lint、check、unit test(2697 件)、`git diff --check` がすべて通った。 +- クロスレビューは対象外(手で編集したソースは 3 ファイルで、認証・スキーマ・公開インターフェースの変更もない)。 From cb44b3dd210283164aeb6692d5e17c2214337f89 Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sun, 11 Oct 2026 05:18:19 +0000 Subject: [PATCH 3/3] docs: remove add-arc-like-contests plan Co-Authored-By: Claude Sonnet 5.5 --- .../2026-10-11/add-arc-like-contests/plan.md | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md diff --git a/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md b/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md deleted file mode 100644 index 564e53f47..000000000 --- a/docs/dev-notes/2026-10-11/add-arc-like-contests/plan.md +++ /dev/null @@ -1,40 +0,0 @@ -# ARC-Like コンテスト 3 件の追加(#4140) - -## 概要 - -[#4140](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/4140) で、`nikkei2019-2-qual`・`ddcc2020-qual`・`diverta2019-2` の全問題を `prisma/tasks.ts` に追加し、`ContestType.ARC_LIKE` に分類した。 -3 件とも ARC 相当とするのはユーザーの判断による(コンテスト ID や問題構成からは機械的に決まらない)。 - -## 決定事項 - -| 項目 | 決定 | 理由 | -| ------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ | -| 追加する問題 | 各コンテストの全問題(A〜F) | `tenka1-2019` のように一部だけ載せる前例もあるが、本件は全問題を対象とした | -| `ARC_LIKE` のコンテスト名 | 公式名(日本語名があれば日本語) | 既存の `nikkei2019-qual` に合わせた。名前は AtCoder のコンテストページでユーザーが確認済み | -| 本戦(`*-final`) | スコープ外 | Issue の対象外(YAGNI) | -| 表示ラベル | 変更しない(コンテスト ID の大文字化のまま) | `ARC_LIKE` にはラベル生成関数がなく、既存の ARC-Like と同じ扱いにそろえる | - -## 前提として判明したこと - -- `ABC_LIKE` / `ARC_LIKE` は完全一致、その他のグループは前方一致で判定する。そのため、既存の `diverta2019` キーでは `diverta2019-2` を拾えず、続編にも個別のキーが要る。変数名(`ContestPrefix`、`arcLikePrefixes`)からは前方一致に見えるので、`prefixes.ts` にコメントを追加した。 -- 問題データの取得元は [problems.json](https://kenkoooo.com/atcoder/resources/problems.json) だが、エージェントからは atcoder.jp に到達できず、WebFetch は problems.json を途中で切り捨てる。今回は該当行をユーザーに貼ってもらった。 -- 参考にした #3951 以降、コンテスト関連のユーティリティは `src/lib/contests/utils/`、テストケースは `src/lib/contests/fixtures/` へ移動している。 - -## 採用しなかった案 - -- `ARC_LIKE` を前方一致に変更する:`diverta2019` などのキーが想定外の ID まで拾ってしまう。 -- ARC-Like 用のラベル生成関数を追加する:要件外で、既存の ARC-Like コンテストの表示まで変わる。 - -## 派生した変更 - -同種の Issue(「問題を追加しましょう」)は繰り返し起きるが、既存の `add-contest-table-provider` skill は ContestType や Provider を新しく追加するときにしか読み込まれない。そこで以下を整備した。 - -- `add-tasks` skill を新設した(`SKILL.md` に概略、`instructions.md` に詳細)。AOJ 側の手順は #3951 の差分から読み取った下書きで、未検証。必要になったときに直す。 -- `add-contest-table-provider` の手順 0 と Layer 3 から `add-tasks` を参照するようにし、データ取得と照合方法の説明を一か所にまとめた。 -- `how-to-add-contest-table-provider.md` の「事前確認」に、完全一致と前方一致の違いを 1 行追記した。 -- Claude は `.claude/skills/` のシンボリックリンクから、Codex は AGENTS.md の skill 一覧から参照する。 - -## 検証 - -- format、lint、check、unit test(2697 件)、`git diff --check` がすべて通った。 -- クロスレビューは対象外(手で編集したソースは 3 ファイルで、認証・スキーマ・公開インターフェースの変更もない)。