# qwen2.5:7b / 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
### Step 3: 验证 `fixtures/article.mdx`

我们已经成功地将修改后的 frontmatter 写入了 `fixtures/article.mdx`。接下来，我们将运行 `node validate.mjs` 来验证其是否通过所有检查。

现在，请允许我进行以下操作：

```bash
node fixtures/validate.mjs
```

这将会执行验证脚本并返回结果。


## Metrics
```json
{
  "ttftMs": null,
  "tokens": 0,
  "tokPerSec": null,
  "wallMs": 15677,
  "toolCalls": 4,
  "toolCallInvalid": 0,
  "capHit": false,
  "errored": false,
  "crashed": false
}
```