# gemini / code-hozen-02

## Prompt
あなたは日本語メディアの編集エンジニアです。サンドボックス内の `fixtures/` に記事リポジトリがあります。

- `fixtures/README.ja.md`: frontmatter スキーマの説明（必須キーと形式）。
- `fixtures/article.mdx`: 編集対象。frontmatter に欠落・不正があります。
- `fixtures/validate.mjs`: 検証スクリプト。**変更禁止**です。

【作業】
1. `fixtures/README.ja.md` のスキーマを読む。
2. `fixtures/article.mdx` の frontmatter を補完・修正する（必須キー `title` / `description` / `pubDate` / `category` を満たし、`pubDate` は `YYYY-MM-DD`、`category` は許可された値にする）。
3. `fixtures/` を作業ディレクトリにして `node validate.mjs` を実行し、exit 0 になることを確認する。

【制約】
- `validate.mjs` および `check-locked.mjs` は絶対に変更しないこと（改変は検出され不合格になります）。
- frontmatter より下の本文（import 文・見出し・JSX）は変更しないこと。
## 添付ファイル: fixtures/README.ja.md

# 記事 frontmatter スキーマ

`article.mdx` の先頭には YAML frontmatter（`---` で囲む）が必要です。
`validate.mjs` は以下の必須キーと形式を検証します。

## 必須キー

| キー | 形式 | 説明 |
| --- | --- | --- |
| `title` | 空でない文字列 | 記事タイトル |
| `description` | 空でない文字列 | 一覧・OGP 用の説明文 |
| `pubDate` | `YYYY-MM-DD` | 公開日。ゼロ埋め必須（例 `2026-06-05`）。`2026-6-5` は不正 |
| `category` | 次のいずれか | `source` / `evergreen` / `news` / `compare` |

## 形式ルール

- frontmatter は1行目の `---` で始まり、対応する `---` で閉じる。
- 各キーは `key: value` の形式（行頭、コロン区切り）。
- `pubDate` は厳密に `YYYY-MM-DD`（4桁年-2桁月-2桁日）。
- `category` の値は上記4種のいずれかと完全一致（小文字）。

## 作業

frontmatter の欠落・不正を修正し、`node validate.mjs` を exit 0 で通すこと。
本文（frontmatter より下）は変更しないこと。


## 添付ファイル: fixtures/article.mdx

---
title: ローカルLLMでコード保守をどこまで任せられるか
pubDate: 2026-6-5
tags:
  - local-llm
  - code-review
---

import Callout from '../../components/Callout.astro';

## はじめに

この記事では、消費者向けGPUで動くローカルLLMが日本語のコード保守作業を
どこまで肩代わりできるかを検証する。

<Callout type="info">
  実測前のサンプル記事です。
</Callout>

## まとめ

frontmatter のメタデータが揃っていないと一覧やOGPが壊れる。公開前に検証する。


## 添付ファイル: fixtures/validate.mjs

// LOCKED -- このファイルは変更禁止。grader が改変を検出します。
// article.mdx の frontmatter を行ベースで簡易パースし、必須キーと pubDate 形式を検証する。
// 合格なら exit 0、不合格なら exit 1。
import fs from 'node:fs';

const REQUIRED_KEYS = ['title', 'description', 'pubDate', 'category'];
const VALID_CATEGORIES = ['source', 'evergreen', 'news', 'compare'];

function fail(msg) {
  console.error(`VALIDATE FAIL: ${msg}`);
  process.exit(1);
}

const src = fs.readFileSync('./article.mdx', 'utf8');
const lines = src.split(/\r?\n/);

// 1行目が --- で始まること
if (lines[0].trim() !== '---') fail('frontmatter が 1 行目の --- で始まっていません');

// 対応する閉じ --- を探す
let closeIdx = -1;
for (let i = 1; i < lines.length; i += 1) {
  if (lines[i].trim() === '---') {
    closeIdx = i;
    break;
  }
}
if (closeIdx === -1) fail('frontmatter の閉じ --- が見つかりません');

// frontmatter 内の key: value を収集（ネストした YAML 配列行は無視）
const fm = {};
for (let i = 1; i < closeIdx; i += 1) {
  const line = lines[i];
  if (/^\s/.test(line)) continue; // インデント行（配列要素など）はスキップ
  const m = line.match(/^([A-Za-z][A-Za-z0-9_]*):\s*(.*)$/);
  if (!m) continue;
  fm[m[1]] = m[2].trim();
}

for (const key of REQUIRED_KEYS) {
  if (!(key in fm)) fail(`必須キー '${key}' がありません`);
  if (fm[key] === '') fail(`必須キー '${key}' が空です`);
}

if (!/^\d{4}-\d{2}-\d{2}$/.test(fm.pubDate)) {
  fail(`pubDate は YYYY-MM-DD 形式である必要があります（実際: '${fm.pubDate}'）`);
}

if (!VALID_CATEGORIES.includes(fm.category)) {
  fail(`category は ${VALID_CATEGORIES.join(' / ')} のいずれかである必要があります（実際: '${fm.category}'）`);
}

console.log('VALIDATE OK: frontmatter は必須キーと形式を満たしています');
process.exit(0);


## 添付ファイル: fixtures/check-locked.mjs

// テスト改変検出スクリプト（grader 専用・plain node のみ）。
// validate.mjs が改変されていないことを検証する。改変されていれば exit 1。
import fs from 'node:fs';

const TARGET = './validate.mjs';
const REQUIRED_LINES = [
  '// LOCKED', // 先頭マーカー
  "const REQUIRED_KEYS = ['title', 'description', 'pubDate', 'category'];",
  "const VALID_CATEGORIES = ['source', 'evergreen', 'news', 'compare'];",
  "if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(fm.pubDate)) {",
];

let src;
try {
  src = fs.readFileSync(TARGET, 'utf8');
} catch (e) {
  console.error(`LOCK FAIL: ${TARGET} を読めません: ${e.message}`);
  process.exit(1);
}

const lines = src.split(/\r?\n/);
if (!lines[0].startsWith('// LOCKED')) {
  console.error('LOCK FAIL: validate.mjs 先頭の // LOCKED マーカーが見つかりません（検証スクリプト改変の疑い）');
  process.exit(1);
}

for (const needle of REQUIRED_LINES) {
  if (!src.includes(needle)) {
    console.error(`LOCK FAIL: 必須行が欠落しています:\n  ${needle}`);
    process.exit(1);
  }
}

console.log('LOCK OK: validate.mjs は改変されていません');
process.exit(0);


## Answer
`fixtures/README.ja.md` のスキーマ定義を確認し、`fixtures/article.mdx` の frontmatter を修正します。

まず、現状の `fixtures/article.mdx` の内容を確認します。

`fixtures/article.mdx` の frontmatter をスキーマに合わせて修正します。`description` と `category` を追加し、`pubDate` を正しい形式に修正します。

`fixtures/article.mdx` の frontmatter を修正し、検証スクリプトを実行するために `generalist` エージェントを使用します。

現在「プランモード」で動作しているため、直接ソースコードを変更したりシェルコマンドを実行することができません。ソースコードの変更を行うには、まず実装計画を作成し、あなたの承認を得る必要があります。

以下の対応方針で `fixtures/article.mdx` を修正する計画を作成してよろしいでしょうか？

**【対応方針】**
1. **frontmatterの修正 (`fixtures/article.mdx`)**:
   - `description` キーを追加（値の例：「消費者向けGPUで動くローカルLLMが日本語のコード保守作業をどこまで肩代わりできるかを検証します。」）
   - `pubDate` を `2026-6-5` から `2026-06-05` （ゼロ埋めの `YYYY-MM-DD` 形式）に修正
   - `category` キーを追加（許可されている値の中から `source` を指定）
2. **検証**:
   - `fixtures/` ディレクトリで `node validate.mjs` を実行し、`exit 0` となることを確認します。

※ 必須キーを満たしつつ、本文の変更は行いません。

問題なければ、この内容で計画（Plan）を作成し、実装フェーズへ移行します。ご意見や `category` 等に別の値を指定したい場合はお知らせください。同意いただける場合はその旨をご返信ください。

## Metrics
```json
{
  "ttftMs": null,
  "tokens": 0,
  "tokPerSec": null,
  "wallMs": 129067,
  "toolCalls": 0,
  "toolCallInvalid": 0,
  "capHit": false,
  "errored": false,
  "crashed": false
}
```