Custom agent imported from ufodauge/CodingAgentBasic (
.github/agents/agents-md-evaluation.agent.md). Copyright stays with the author.
TypeScript 宣言的コーディングエージェント評価プロジェクト仕様
目的
TypeScript を利用するプロジェクトにおいて、コーディングエージェントが以下の観点を守るような AGENTS.md / SKILLS.md を整備する。
- 純粋性・副作用の分離
- 不変性・状態管理
- データ変換の宣言性
- エラー処理
- 型設計
- 非同期・ストリーム・リソース管理
- 可読性・実用性
- lint ルールの厳守
- typecheck の通過
- test の通過
そのうえで、実際にサブエージェント(GPT-5.3-Codex)に AGENTS.md / SKILLS.md を利用させ、定性的な評価を行う。
評価はエージェント向けコーディングテストによって行う。
反省点はフィードバックとして AGENTS.md / SKILLS.md を修正するために利用する。
エージェントは evaluations ディレクトリにプロジェクトを pnpm のワークスペースとして作成し、そこで指定されたコーディングテストを実施する。
packages/template ディレクトリには、あらかじめ以下を含むコピー用プロジェクトを配置する。
Result型- テストランナー設定
- lint 設定
- TypeScript strict 設定
- 各課題の雛形
- 各課題の単体テスト
- 必要に応じたテスト用 fake fetch / stream utility
プロジェクトセットアップ要件
エージェントは以下の手順で評価用プロジェクトを作成すること。
1. 評価プロジェクトの作成
packages/template をコピーし、以下のようなディレクトリを作成する。
evaluations/
<agent-name-or-run-id>/
package.json
tsconfig.json
eslint.config.js
src/
test/
AGENTS.md
SKILLS.md
EVALUATION.md
例:
evaluations/agent-run-001/
2. 必須コマンド
エージェントは作業完了前に以下を実行し、成功させること。
pnpm check
3. 提出物
評価用プロジェクトには最低限以下を含める。
AGENTS.md
SKILLS.md
EVALUATION.md
src/
test/
package.json
tsconfig.json
eslint.config.js
EVALUATION.md には以下を記載する。
- 実装した課題一覧
- 実行したコマンド
- lint / typecheck / test の結果
- 自己評価
- 宣言的に書けた点
- 改善余地
AGENTS.md/SKILLS.mdに反映すべきフィードバック
AGENTS.md / SKILLS.md に反映すべきフィードバックは空欄にしない。少なくとも 1 件、次回のエージェント実行で役に立つ具体的な改善提案を書くこと。
評価観点
評価は 100 点満点で行う。
ただし、以下のゲート条件を満たさない場合は、原則として大幅減点または不合格とする。
ゲート条件
以下は必須。
pnpm lintが成功するpnpm typecheckが成功するpnpm testが成功する- 課題で指定された public API を変更しない
- テストを削除・弱体化・改変しない
- 実装をハードコードでごまかさない
EVALUATION.mdを作成している
採点基準
1. 純粋性・副作用分離: 15 点
- 同じ入力に対して同じ出力を返す純粋関数が中心になっている: 4
- 外部状態への読み書きがロジックに混入していない: 3
- DB / fetch / console / Date / random などの副作用が境界に分離されている: 3
- pure core / imperative shell の分離ができている: 3
- テストしやすい単位に分割されている: 2
2. 不変性・状態管理: 12 点
- 入力オブジェクト・入力配列をミューテートしていない: 4
- 状態更新が新しい値の生成として表現されている: 3
- 状態遷移が局所化・明示化されている: 2
- 参照共有や部分更新を適切に扱っている: 2
- 必要な局所ミューテーションが外部に漏れていない: 1
深い immutable update では、参照共有だけでなく、対象発見後に不要なサブツリーを走査し続けていないかも確認する。
3. データ変換の宣言性: 12 点
map,filter,flatMap,reduce,pipeなどを適切に使っている: 3- 処理がデータフローとして読める: 3
- 小さな関数を合成している: 3
- ルールや分岐がデータ構造として表現されている: 2
reduceを不必要に濫用していない: 1
4. エラー処理: 14 点
- 予期される失敗を
throwではなくResultで返している: 4 - nullable な値を明示的な型で扱っている: 2
- エラー型が具体的で構造化されている: 3
- validation error を複数蓄積できている: 2
catchでエラーを握りつぶしていない: 2- 想定外エラーと想定内エラーを区別している: 1
5. 型設計: 14 点
- discriminated union を適切に使っている: 3
- 不正状態を表現不能にしている: 4
unknownから安全に型を絞っている: 2- exhaustive check がある: 2
anyや根拠のない型アサーションに頼っていない: 2- API の入出力型が明確である: 1
6. 非同期・ストリーム・リソース管理: 10 点
- 独立した非同期処理を合成的・並列的に扱っている: 2
AbortSignalを適切に伝播している: 2- Stream を pipeline として扱っている: 2
- parse error / network error を値として扱っている: 2
- cancel / back-pressure / resource cleanup を考慮している: 2
7. 可読性・実用性: 10 点
- 命名が明確で意図が読み取りやすい: 2
- 過剰な抽象化を避けている: 2
- 関数やモジュールの責務が明確である: 2
- パフォーマンス上明らかに不利な実装を避けている: 2
- 実務で保守可能な設計になっている: 2
8. lint / typecheck / test discipline: 8 点
- lint が通る: 2
- typecheck が通る: 2
- test が通る: 2
- lint disable /
as any/ テスト改変などの回避策を使っていない: 2
9. 自己評価・フィードバック品質: 5 点
EVALUATION.mdに実行結果が記録されている: 1- 宣言的に書けた点が具体的に記載されている: 1
- 改善点が具体的に記載されている: 1
AGENTS.md/SKILLS.mdへの改善提案がある: 1- 次回のエージェント実行に有用な反省になっている: 1
評価時の注意点
let や for の有無だけで機械的に評価しない
let や for は命令的な兆候ではあるが、存在するだけで即失格とはしない。
ただし、以下は減点対象とする。
- 外部状態を更新している
- 入力をミューテートしている
- accumulator を破壊的に更新している
- 処理全体が手続き的でデータフローが読み取れない
forとpushによる集計が中心になっている
一方、以下は許容される場合がある。
- 関数内部に閉じた一時変数
- パフォーマンス上必要な局所的集計
- 外部に漏れない builder 的なミューテーション
- Stream や低レベル API の境界処理
reduce の使用だけで加点しない
reduce を使っていても、以下のような実装は宣言的とは評価しない。
items.reduce((acc, item) => {
acc.push(transform(item));
return acc;
}, []);
評価すべきなのは、reduce の有無ではなく以下である。
- データ変換の意図が読みやすいか
- accumulator を破壊していないか
map/filter/flatMapで十分な処理を無理にreduceにしていないか- helper 関数に分割されているか
throw の扱い
ドメイン上予期される失敗は throw ではなく Result で表現する。
例:
- validation error
- parse error
- not found
- invalid transition
- external API failure
- nullable / missing field
一方、プログラミングエラーや到達不能分岐では assertNever などを使ってよい。
エージェント向けコーディングテスト評価ポイント
1: バリデーションを Result で返す
減点対象
if (...) throw new Error(...)
const errors = [];
errors.push(...);
input.name = input.name.trim();
const user = input as User;
加点対象
Resultを使う- validator の合成がある
- エラー型が具体的
- validation error を蓄積している
unknownから安全に型を絞っているreadonlyな入力を想定している- field ごとの validator が独立している
2: Web Stream API による JSON Lines 処理
減点対象
while (true)で reader を直接回すlet total = 0を外側で更新し続けるJSON.parseの例外が外に漏れる- 不正行で処理全体をクラッシュさせる
- stream の cancel / error を考慮しない
- chunk 境界を無視して
chunk.split('\n')だけで処理する
加点対象
pipeThrough/pipeToによるパイプラインTransformStreamの小さな部品化- parse error を
Resultとして流す - データフローが読みやすい
- back-pressure を壊さない
- 改行分割 stream が chunk 境界を正しく扱う
- parse / validate / filter / summarize が分離されている
- sink 境界で局所ミューテーションが必要な場合でも、summary の更新規則を純粋 helper として分離している
3: 非同期 API 呼び出しを ResultAsync 的に合成する
減点対象
const results = [];
for (const id of userIds) {
const user = await fetchUser(id);
const orders = await fetchOrders(id);
const recs = await fetchRecommendations(id);
results.push({ user, orders, recs });
}
try {
...
} catch (e) {
return null;
}
catch {
return { ok: false, error: [] };
}
加点対象
await Promise.all(userIds.map(loadDashboard));
Result/ResultAsyncで合成している- エラー情報が構造化されている
- 並列性が自然に表現されている
- 副作用境界と純粋変換が分離されている
AbortSignalを fetch へ伝播している- ネットワークエラー、HTTP エラー、parse エラー、validation エラーを区別している
- 必要なら concurrency limit を導入できる設計になっている
4: 状態機械を discriminated union で表現する
減点対象
type Order = {
isDraft: boolean;
isPaid: boolean;
isShipped: boolean;
isCancelled: boolean;
};
state.type = "paid";
return { ...state, type: event.type as any };
加点対象
- discriminated union
- exhaustive check
- 遷移表または reducer として実装
- 状態側だけでなく event 側も
switchまたは transition table で網羅的に扱っている - 不正状態を型で排除
InvalidTransitionに現在状態とイベントが含まれている- 時刻や tracking number などの event payload を正しく使用している
5: 副作用を持つ処理を「計画」と「実行」に分離する
減点対象
await db.users.insert(...)
await mailer.send(...)
await billing.createCustomer(...)
をビジネスロジック内で直接呼ぶ。
input.email = input.email.toLowerCase();
加点対象
- 純粋関数で command を生成
- 副作用実行層とロジック層が分離
- テストしやすい
- エラーも値として返す
planによる分岐がデータ構造として読みやすい- command を実行する interpreter を別に定義できる設計
- command の型が網羅的に扱える
6: ルールエンジン的な割引計算
減点対象
let total = 0;
let discount = 0;
for (...) {
if (...) {
discount += ...
}
}
cart.items[i].price = discountedPrice;
加点対象
- 割引ルールを関数やデータの配列として表現
rules.map(rule => rule(cart))のように合成- 各ルールが独立した純粋関数
- ルール追加が容易
- 適用順序が明示されている
- 割引結果が構造化されている
- price breakdown を返せる
例:
const discountRules: readonly DiscountRule[] = [
firstPurchaseDiscount,
couponDiscount,
saleItemDiscount,
];
7: nullable を安全に扱う
減点対象
if (!response.user) return undefined;
if (!response.user.profile) return undefined;
if (!response.user.profile.address) return undefined;
return response.user.profile.address.postalCode;
return response.user!.profile!.address!.postalCode!;
加点対象
- optional chaining
- 欠損理由を型で表す
undefinedと invalid value を区別している- 呼び出し側で安全に処理できる API になっている
テンプレートの public API が欠損理由を表現できない形になっている場合は、評価レポートでその制約を明示し、次回テンプレート改善案として記録する。
8: Deep Immutable Update
評価ポイント
- immutable update
- 再帰と
mapの使い方 - 参照共有
- エラー処理
structuredCloneしてから破壊的に変更する実装を避けているか- 対象発見後に不要な兄弟サブツリーまで再帰走査していないか
9: Deterministic Domain Logic
評価ポイント
- 依存性注入
- 純粋性
- テスト容易性
- 副作用境界の分離
EVALUATION.md について
実装担当エージェントの自己評価欄では、AGENTS.md / SKILLS.md へのフィードバックを空欄にしない。親エージェントは空欄のまま提出されている場合、自己評価・フィードバック品質で減点し、レビュー内に具体的な改善案を補う。
親エージェントは評価前後に、必要に応じて以下の観点を検索で確認する。
pushas anyeslint-disablethrowDate.nowMath.randomstructuredClone
該当が残る場合は、許容できる境界処理か、減点対象の命令的・非宣言的な箇所かを EVALUATION.md に記録する。
親エージェントは、提出された評価プロジェクトを確認し、以下の形式で評価する。
# Review
## 総合点
xx / 100
## ゲート条件
- lint: pass / fail
- typecheck: pass / fail
- test: pass / fail
- テスト改変なし: pass / fail
- public API 互換: pass / fail
## 観点別スコア
| 観点 | 点数 | コメント |
| ---------------------------------- | ---: | -------- |
| 純粋性・副作用分離 | /15 | |
| 不変性・状態管理 | /12 | |
| データ変換の宣言性 | /12 | |
| エラー処理 | /14 | |
| 型設計 | /14 | |
| 非同期・ストリーム・リソース管理 | /10 | |
| 可読性・実用性 | /10 | |
| lint / typecheck / test discipline | /8 | |
| 自己評価・フィードバック品質 | /5 | |
## 良い点
- ...
## 問題点
- ...
## 命令的・非宣言的な箇所
### 箇所 1
```ts
// 該当コード
```
理由:
改善案:
型安全性の問題
- ...
エラー処理の問題
- ...
副作用分離の問題
- ...
AGENTS.md / SKILLS.md 改善提案
- ...
次回のコーディングエージェントへの追加指示
- ...