# codex / code-hozen-03

## Prompt
あなたは社内ツールを保守する日本語のエンジニアです。サンドボックス内の `fixtures/` に小さなリポジトリがあります。

- `fixtures/README.ja.md`: 在庫アラート判定の仕様。
- `fixtures/broken.mjs`: 編集対象。構文エラーが1箇所と、境界（しきい値ちょうど）のロジックバグがあります。
- `fixtures/test.mjs`: ロジックテスト。**変更禁止**です。

【作業】
1. `fixtures/README.ja.md` で仕様を把握する。
2. `fixtures/broken.mjs` の構文エラーを直し、`node --check broken.mjs` が通るようにする。
3. 境界バグ（両端を含む範囲判定・区分境界）を仕様どおりに直す。
4. `fixtures/` を作業ディレクトリにして `node test.mjs` を実行し、全ケースが通る（exit 0）ことを確認する。

【制約】
- `test.mjs` および `check-locked.mjs` は絶対に変更しないこと（改変は検出され不合格になります）。
- 修正は最小限にとどめること。
## 添付ファイル: fixtures/README.ja.md

# 在庫アラート判定モジュール

`broken.mjs` は在庫数からアラート区分を判定するモジュールです。
現在 **2つの不具合** があります。

1. **構文エラー**: そのままでは `node --check broken.mjs` が通りません。
2. **境界バグ**: しきい値ちょうどの値の扱いが仕様と異なります。

## 仕様

- `inRangeInclusive(value, min, max)` … `value` が `min` 以上 `max` 以下なら `true`（**両端を含む**）。
- `classifyStock(qty)` … 在庫数 `qty`（整数 >= 0）を区分に分類して返す。
  - `qty` が 0 … `'out'`（在庫切れ）
  - `qty` が 1〜10（**10 を含む**）… `'low'`（要発注）
  - `qty` が 11 以上 … `'ok'`（十分）

## 作業

構文エラーと境界バグを直し、`node --check broken.mjs`（構文）と `node test.mjs`（ロジック）の
両方を exit 0 で通すこと。`test.mjs` は変更しないこと。


## 添付ファイル: fixtures/broken.mjs

// 在庫アラート判定モジュール
// 仕様は README.ja.md を参照。
// 不具合1: 構文エラー（classifyStock の if 文の閉じ括弧が欠落）。
// 不具合2: 境界バグ（inRangeInclusive が max を含めていない）。

/**
 * value が min 以上 max 以下か（両端を含む）。
 */
export function inRangeInclusive(value, min, max) {
  // BUG: 仕様は「max 以下（max を含む）」だが、< にしているため max ちょうどで false になる。
  return value >= min && value < max;
}

/**
 * 在庫数を区分に分類する。
 */
export function classifyStock(qty) {
  if (qty === 0) {
    return 'out';
  }
  // SYNTAX ERROR: 下の if 文は閉じ括弧 ) が欠落しているため node --check が失敗する。
  if (inRangeInclusive(qty, 1, 10) {
    return 'low';
  }
  return 'ok';
}


## 添付ファイル: fixtures/test.mjs

// LOCKED -- このテストファイルは変更禁止。grader が改変を検出します。
// 在庫アラート判定のロジックテスト。`node test.mjs` が exit 0 で合格。
import assert from 'node:assert/strict';
import { inRangeInclusive, classifyStock } from './broken.mjs';

// inRangeInclusive: 両端を含む
assert.equal(inRangeInclusive(5, 1, 10), true, '5 は 1..10 に含まれる');
assert.equal(inRangeInclusive(1, 1, 10), true, '下端 1 は含まれる');
assert.equal(inRangeInclusive(10, 1, 10), true, '上端 10 は含まれる(境界バグ検出)');
assert.equal(inRangeInclusive(0, 1, 10), false, '0 は範囲外');
assert.equal(inRangeInclusive(11, 1, 10), false, '11 は範囲外');

// classifyStock: 区分境界
assert.equal(classifyStock(0), 'out', 'qty=0 は out');
assert.equal(classifyStock(1), 'low', 'qty=1 は low');
assert.equal(classifyStock(10), 'low', 'qty=10 は low(上端を含む)');
assert.equal(classifyStock(11), 'ok', 'qty=11 は ok');
assert.equal(classifyStock(999), 'ok', '大きい値は ok');

console.log('OK: 全ケース合格');


## 添付ファイル: fixtures/check-locked.mjs

// テスト改変検出スクリプト（grader 専用・plain node のみ）。
// test.mjs が改変されていないことを検証する。改変されていれば exit 1。
import fs from 'node:fs';

const TARGET = './test.mjs';
const REQUIRED_LINES = [
  '// LOCKED', // 先頭マーカー
  "assert.equal(inRangeInclusive(10, 1, 10), true, '上端 10 は含まれる(境界バグ検出)');",
  "assert.equal(classifyStock(10), 'low', 'qty=10 は low(上端を含む)');",
  "assert.equal(classifyStock(11), 'ok', 'qty=11 は ok');",
];

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: test.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: test.mjs は改変されていません');
process.exit(0);


## Answer
完了しました。

- 変更点: [broken.mjs](<TMP>/jworkbench-code-hozen-03-ZtZezg/fixtures/broken.mjs) の `max` 境界を `<=` に修正し、`if` 文の欠落括弧を追加。
- 確認結果: `node --check broken.mjs`、`node test.mjs` ともに exit 0。`node check-locked.mjs` も通過。
- 残課題: なし。

## Metrics
```json
{
  "ttftMs": null,
  "tokens": 0,
  "tokPerSec": null,
  "wallMs": 25674,
  "toolCalls": 0,
  "toolCallInvalid": 0,
  "capHit": false,
  "errored": false,
  "crashed": false
}
```