Skip to content

feat(tools): mechanical diagnosis of harvested review runs - #44

Open
Kizunad wants to merge 2 commits into
mainfrom
fix/diagnostic-tool
Open

Kizunad wants to merge 2 commits into
mainfrom
fix/diagnostic-tool

Conversation

@Kizunad

@Kizunad Kizunad commented Aug 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

The W11 hand analysis — digging terminal reason / status code / model out of infrastructure_failure artifacts — is now mechanical: node tools/diagnose.mjs <artifact-dir> prints the decision, one line per stage failure and coverage gap (terminal reason, HTTP status, model, attempts, retryable where the artifact carries them), and an explicit final status.

  • Classification reads the diagnostic events, not the collapsed after N attempts: claude exited 1 string: init model, api_retry trail, and the confirmed single-result envelope under the engine's own disclosure gate (10f9ef7)
  • Works on pre-contract artifacts today: terminal reason, status code, model and the retry trail are all recoverable from old diagnostics; engine-level attempts and retryable only exist in the new structured fields and are reported unknown rather than guessed
  • Absent is never negative: failures and gaps without a diagnostic print UNCLASSIFIED with the reason named, and the run status line only ever says CLEAN when there are literally no failures and no gaps — the exact confusion W11 hit (coverage gap with no diagnostic reading as clean) is the sixth instance, this is not the seventh
  • New-shape structured fields (post-fix(review): publish the diagnostic contract on stage failures and coverage gaps #43) classify directly and take precedence over diagnostic-derived values

Test plan

  • 14 new tests (237/0/6 full suite): each cause class against fixtures — old-shape 503 summary/plan, old-shape 400 tool_choice, old-shape gap (unclassifiable, never clean), no-diagnostic infra, new-shape cpu gate, new-shape gap, schema-exhausted determinism, approve-clean, corrupt diagnostic, malformed infra, unconfirmed-result gate, missing artifact
  • Fixtures for the validation runs are verbatim corpus artifacts
  • Validated against the W11-known answers: PR 2025 died at summary on a 503 (cc-review-lite, 10 CLI retries), PR 2026 died at plan on a 503 (cc-review), PR 1993 decided request_changes with one unclassifiable find:correctness gap — all three reproduced exactly
  • Swept all 169 artifact-bearing harvested runs: 60 CLEAN, 51 all-classified failures, 16 decided-with-unclassified-gap, no crashes; 5 empty harvest dirs correctly report no review.json found

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新功能

    • 新增诊断报告工具,可读取一个或多个评审结果并生成结构化报告。
    • 支持识别清洁、失败、已决策、格式错误和未分类结果。
    • 报告包含终止原因、HTTP 状态、模型、重试次数及相关证据。
    • 支持覆盖缺口、基础设施失败、API 错误、schema 错误及损坏数据的分类。
  • 测试

    • 增加对新旧诊断格式、异常场景、报告格式和清洁结果判定的全面测试。

Turns the W11-style hand analysis of infrastructure_failure artifacts into a
tool: node tools/diagnose.mjs <artifact-dir> prints, per run, the decision,
one line per stage failure and coverage gap with terminal reason, HTTP status,
model, attempts and retryable when the artifact carries them, and an explicit
final status that never lets "unclassified" read as "clean".

Classification reads the diagnostic events (init model, api_retry trail,
confirmed result envelope with the engine's own one-result disclosure gate),
not the collapsed "after N attempts: claude exited 1" string. Pre-contract
artifacts recover terminal reason, status code and model from the events, and
the retry trail; engine-level attempts and retryable are only in the new
structured fields and are reported unknown rather than guessed. Failures and
gaps without a diagnostic are UNCLASSIFIED with the reason named - absence of
evidence is not evidence of health.

Validated against the W11-known runs: PR 2025 summary 503, PR 2026 plan 503,
PR 1993 decided with one unclassifiable find:correctness gap.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: DIAGNOSTIC-TOOL
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Kizunad, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d82bef1a-0994-4e41-8190-c5499cd7e60b

📥 Commits

Reviewing files that changed from the base of the PR and between 22f6b3f and be9bd94.

📒 Files selected for processing (2)
  • test/diagnose.test.mjs
  • tools/diagnose.mjs
📝 Walkthrough

Walkthrough

新增 tools/diagnose.mjs,用于读取诊断工件、分类失败与覆盖缺口,并生成报告。新增测试和 fixture,覆盖旧、新诊断格式、清洁结果、损坏数据及工件加载错误。

Changes

诊断工具与验证

Layer / File(s) Summary
诊断数据解析与失败标准化
tools/diagnose.mjs
新增 parseDiagnostic、classifyEvents 和 classifyFailure。工具解析诊断事件,识别 API 错误,并合并结构化失败字段。
覆盖缺口分类与报告状态
tools/diagnose.mjs
新增覆盖缺口分类、CLEAN、FAILED、DECIDED、MALFORMED 状态判定及报告格式化。未分类结果不会被判定为 clean。
工件发现与命令行执行
tools/diagnose.mjs
工具读取顶层或唯一嵌套的 review.json,读取可选 manifest.json,并处理多个 artifact 目录及错误退出状态。
诊断场景与回归验证
test/diagnose.test.mjs, test/fixtures/diagnose/*
新增测试和 fixture,覆盖 503、400、schema 错误、CPU 门控、coverage gap、清洁结果、损坏诊断、未确认结果事件和缺失 review.json。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Kizunad/review#8:诊断解析器测试 Claude runner 产生的流、API 重试和基础设施失败数据。
  • Kizunad/review#14:测试 v2 评审决策、失败、coverage gap 和结构化诊断契约。

Poem

小兔敲开诊断门,
失败重试看得真。
缺口、错误都记录,
清洁结果不混淆。
报告生成跳一跳。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了新增诊断工具及其对已采集评审运行的机械化诊断功能。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/diagnostic-tool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/diagnose.test.mjs`:
- Around line 131-136: Update classifyReview so the clean decision requires
decision === 'approve' in addition to empty failures and coverageGaps; preserve
the existing CLEAN status behavior for valid approvals. Extend the diagnose
tests with a request_changes fixture or case having both arrays empty and assert
that it is not classified as clean.
- Around line 187-189: Add the required tracked placeholder file at
test/fixtures/diagnose/empty/.gitkeep so the empty fixture directory exists in
clean checkouts and loadReview(fixtures('empty')) reaches the expected “no
review.json found” error.

In `@test/fixtures/diagnose/new-structured-cpu-gate/review.json`:
- Around line 12-18: 更新 new-structured-cpu-gate fixture 的 diagnostic 内容,使其中的
apiErrorStatus、terminalReason、model 和 apiErrorMessage 与外层结构化字段存在冲突;同时调整
test/diagnose.test.mjs 中对应断言,明确验证诊断结果优先采用外层结构化字段,而不是 diagnostic 内的值。

In `@tools/diagnose.mjs`:
- Around line 283-285: Update the entry-point guard surrounding main to use Node
24’s native import.meta.main check instead of comparing import.meta.url with a
manually constructed process.argv path, preserving the existing
main(process.argv.slice(2)) invocation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ed3476ec-5f8f-4862-9e1b-6648d076fc54

📥 Commits

Reviewing files that changed from the base of the PR and between 77a664b and 22f6b3f.

📒 Files selected for processing (16)
  • test/diagnose.test.mjs
  • test/fixtures/diagnose/approve-clean/review.json
  • test/fixtures/diagnose/corrupt-diag/review.json
  • test/fixtures/diagnose/empty/.keep
  • test/fixtures/diagnose/malformed-infra/review.json
  • test/fixtures/diagnose/new-gap-with-diagnostic/review.json
  • test/fixtures/diagnose/new-structured-cpu-gate/review.json
  • test/fixtures/diagnose/old-400-toolchoice/review.json
  • test/fixtures/diagnose/old-503-plan/review.json
  • test/fixtures/diagnose/old-503-summary/manifest.json
  • test/fixtures/diagnose/old-503-summary/review.json
  • test/fixtures/diagnose/old-gap-correctness/manifest.json
  • test/fixtures/diagnose/old-gap-correctness/review.json
  • test/fixtures/diagnose/old-no-diag-infra/review.json
  • test/fixtures/diagnose/schema-exhausted/review.json
  • tools/diagnose.mjs

Comment thread test/diagnose.test.mjs
Comment on lines +131 to +136
test('an approve run with no failures and no gaps is the only thing that reads clean', async () => {
const review = await loadFixture('approve-clean');
const classified = classifyReview(review);
assert.equal(classified.clean, true);
assert.match(classified.status, /CLEAN/);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

修正 clean 的决策条件并添加反例测试。

Line 131 声明只有 approve 可以是 clean。tools/diagnose.mjs:153-156 当前只检查 failures 和 coverageGaps。因此,无失败且无 coverage gap 的 request_changes 结果会被报告为 CLEAN。

让 decision === 'approve' 成为 clean 的必要条件。添加 request_changes 且两个数组为空的断言。

建议的测试补充
 test('an approve run with no failures and no gaps is the only thing that reads clean', async () => {
   const review = await loadFixture('approve-clean');
   const classified = classifyReview(review);
   assert.equal(classified.clean, true);
   assert.match(classified.status, /CLEAN/);
+
+  const requestChanges = classifyReview({
+    decision: 'request_changes',
+    failures: [],
+    coverageGaps: [],
+  });
+  assert.equal(requestChanges.clean, false);
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('an approve run with no failures and no gaps is the only thing that reads clean', async () => {
const review = await loadFixture('approve-clean');
const classified = classifyReview(review);
assert.equal(classified.clean, true);
assert.match(classified.status, /CLEAN/);
});
test('an approve run with no failures and no gaps is the only thing that reads clean', async () => {
const review = await loadFixture('approve-clean');
const classified = classifyReview(review);
assert.equal(classified.clean, true);
assert.match(classified.status, /CLEAN/);
const requestChanges = classifyReview({
decision: 'request_changes',
failures: [],
coverageGaps: [],
});
assert.equal(requestChanges.clean, false);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/diagnose.test.mjs` around lines 131 - 136, Update classifyReview so the
clean decision requires decision === 'approve' in addition to empty failures and
coverageGaps; preserve the existing CLEAN status behavior for valid approvals.
Extend the diagnose tests with a request_changes fixture or case having both
arrays empty and assert that it is not classified as clean.

Comment thread test/diagnose.test.mjs
Comment on lines +187 to +189
test('a missing review.json is an error, not a clean result', async () => {
await assert.rejects(loadReview(fixtures('empty')), /no review.json found/);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

提交 empty fixture 的占位文件。

Git 不保留空目录。当前变更没有提交 test/fixtures/diagnose/empty 的可跟踪文件。干净检出后,loadReview() 会抛出 cannot read artifact directory,所以此断言不会匹配 no review.json found。

添加 test/fixtures/diagnose/empty/.gitkeep,以保留该目录。

建议的 fixture 文件
+++ test/fixtures/diagnose/empty/.gitkeep
+Keep this directory in Git for the missing-review fixture.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('a missing review.json is an error, not a clean result', async () => {
await assert.rejects(loadReview(fixtures('empty')), /no review.json found/);
});
Keep this directory in Git for the missing-review fixture.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/diagnose.test.mjs` around lines 187 - 189, Add the required tracked
placeholder file at test/fixtures/diagnose/empty/.gitkeep so the empty fixture
directory exists in clean checkouts and loadReview(fixtures('empty')) reaches
the expected “no review.json found” error.

Comment on lines +12 to +18
"diagnostic": "{\"events\":[{\"type\":\"result\",\"subtype\":\"success\",\"isError\":true,\"apiErrorStatus\":503,\"terminalReason\":\"api_error\"}],\"stderr\":\"\"}",
"apiErrorStatus": 503,
"apiErrorMessage": "Service Unavailable",
"terminalReason": "api_error",
"model": "cc-review",
"attempts": 1,
"retryable": true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

让 fixture 真正验证结构化字段优先级。

当前 diagnostic 中的 apiErrorStatus 和 terminalReason 与结构化字段相同。diagnostic 也没有 model 或 apiErrorMessage。因此,test/diagnose.test.mjs 的 Line 103-114 不能证明冲突时结构化字段优先。错误地优先读取 diagnostic 的实现仍可能通过状态和终止原因断言。请加入带有冲突值的 diagnostic,或增加专门的冲突 fixture,并断言结构化字段覆盖 diagnostic。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/fixtures/diagnose/new-structured-cpu-gate/review.json` around lines 12 -
18, 更新 new-structured-cpu-gate fixture 的 diagnostic 内容,使其中的
apiErrorStatus、terminalReason、model 和 apiErrorMessage 与外层结构化字段存在冲突;同时调整
test/diagnose.test.mjs 中对应断言,明确验证诊断结果优先采用外层结构化字段,而不是 diagnostic 内的值。

Comment thread tools/diagnose.mjs
Comment on lines +283 to +285
if (import.meta.url === `file://${process.argv[1]}`) {
await main(process.argv.slice(2));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Node.js detect ES module run as main script import.meta.filename pathToFileURL

💡 Result:

To determine if an ES module is being run as the main script in Node.js, you can use the import.meta.main property, which was added to stabilize this pattern in newer versions of Node.js [1][2]. For environments or Node.js versions where import.meta.main is not yet available, the standard approach is to compare the module's URL to the entry point path [3][4]. Using import.meta.main (Recommended for modern Node.js) As of Node.js v24+, import.meta.main provides a direct, native boolean check, serving as the ESM equivalent to the CommonJS require.main === module pattern [1][2]. if (import.meta.main) { // Module is run as the main entry point } Alternative Method (For older Node.js versions) In environments without import.meta.main, you can compare import.meta.filename (or fileURLToPath(import.meta.url)) against the process entry point found in process.argv[1] [2][5][4]. import { pathToFileURL } from 'node:url'; // Using import.meta.filename (Node.js v20.11.0+) if (import.meta.filename === process.argv[1]) { // Module is run as the main entry point } // Universal approach (using URL comparison) if (import.meta.url === pathToFileURL(process.argv[1]).href) { // Module is run as the main entry point } Note: import.meta.filename is available as a stable property in recent Node.js versions (v24.0.0+) and provides the absolute path to the module, equivalent to filename in CommonJS [5][6]. If you are working with symlinks or specific CLI tools, ensuring path resolution is handled consistently via realpathSync or similar utilities can help prevent mismatches [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Node version:\n'
node -e 'console.log(process.version)'

printf '\nNode pathToFileURL behavior for Windows-style path (simulated environment):\n'
node - <<'JS'
const { pathToFileURL } = await import('node:url');
const inputs = [
  'tools/diagnose.mjs',
  '/absolute/tools/diagnose.mjs',
  'C:\\path\\tools\\diagnose.mjs',
  'C:/path/tools/diagnose.mjs',
  '/path/with spaces/tools/diagnose.mjs',
  '/path/中文/tools/diagnose.mjs'
];
for (const input of inputs) {
  try {
    console.log(`${input} -> ${pathToFileURL(input).href}`);
  } catch (error) {
    console.log(`${input} -> error: ${error.name}: ${error.message}`);
  }
}
JS

printf '\nStatic diff/stat context:\n'
git diff --stat HEAD~1..HEAD -- tools/diagnose.mjs || true
printf '\nRelevant source lines:\n'
cat -n tools/diagnose.mjs | sed -n '1,20p;270,288p'

Repository: Kizunad/review

Length of output: 2484


🌐 Web query:

Node.js import.meta.main added when deprecated import.meta.url comparison paths

💡 Result:

The import.meta.main property was introduced in Node.js to provide a standard, reliable way to determine if an ES module is the entry point of the current process, serving as the ESM equivalent to the CommonJS require.main === module check [1][2]. Before the introduction of import.meta.main, developers frequently used a comparison between import.meta.url and process.argv[3] (often converted to a file URL) to detect if a script was executed directly [4][5]. However, this approach was widely considered fragile because it could fail in various scenarios, such as when dealing with symbolic links (e.g., npm bin scripts) or different module resolution contexts [5]. With the release of Node.js v24.2.0 and v22.18.0, import.meta.main was implemented as a native, boolean property on the import.meta object [3][1]. This implementation aligns with established patterns in other runtimes like Deno, providing a consistent and robust mechanism for this common requirement [6][5][7]. While the earlier comparison-based methods are not explicitly deprecated in a formal API sense, they are effectively superseded by import.meta.main, which is now the recommended approach for detecting the entry point in ESM [1]. Developers are encouraged to transition to import.meta.main to avoid the pitfalls associated with manual path comparisons [5][8].

Citations:


🌐 Web query:

node 24.15 import.meta.main

💡 Result:

In Node.js, import.meta.main is a boolean property available within ECMAScript modules (ESM) that indicates whether the current module is the entry point of the Node.js process [1][2]. It evaluates to true if the module is the entry point and false otherwise [1][2]. This feature was introduced in Node.js v24.2.0 (and backported to v22.18.0) to provide an ESM-native equivalent to the CommonJS pattern of checking require.main === module [1][3][2]. It is also analogous to Python's if name == "main": idiom [1][2]. Usage Example: export function foo { return 'Hello, world'; } function main { console.log(foo); } // Only execute main if this module is the entry point if (import.meta.main) { main; } The property is currently marked as having an early development stability status (1.0) [1][2].

Citations:


🌐 Web query:

Node v24.15.0 release notes import.meta.main

💡 Result:

Node.js v24.15.0, released on April 15, 2026, does not introduce the import.meta.main property [1][2]. The import.meta.main property was introduced earlier in Node.js v24.2.0 [3][4]. In Node.js, import.meta.main is a boolean property available within ECMAScript modules (ESM) [4]. It returns true if the current module is the entry point of the process and false otherwise [4]. It serves as an ESM-compatible alternative to the CommonJS require.main === module pattern, and is also analogous to the name == "main" check used in Python [4]. Since its introduction in v24.2.0, this property has been a standard feature in all subsequent versions of Node.js, including v24.15.0 [4][5].

Citations:


🌐 Web query:

Node.js v24.15.0 changelog import.meta.main

💡 Result:

The import.meta.main property was not introduced in Node.js v24.15.0; it was originally implemented and added in Node.js v24.2.0 [1][2][3]. In Node.js, import.meta.main is a boolean property available in ECMAScript modules (ESM) that indicates whether the current module is the entry point of the current process [4][1]. It serves as the ESM equivalent to the CommonJS require.main === module check [4][5]. Key details regarding import.meta.main include: - Functionality: Returns true if the module is the entry point, and false otherwise [4]. - History: It was implemented in Node.js v24.2.0 and later backported to v22.18.0 [4][1][6]. - Usage: It is designed to allow developers to conditionally run code only when a file is executed directly from the command line, rather than when it is imported as a module [4][1]. The v24.15.0 release (April 2026) focused on other updates, such as the addition of the --max-heap-size option, --require-module/--no-require-module, and various other minor improvements and bug fixes, but it did not introduce import.meta.main [7][8].

Citations:


入口判断不可移植,使用 import.meta.main。

直接比较 import.meta.url === file://${process.argv[1]} 会因为手工拼接 URL、路径百分号编码、Windows 路径格式等原因失败,导致诊断 CLI 在部分环境不执行。本项目使用 Node 24,可改用 ESM 原生的入口判断方式 if (import.meta.main),无需手动比较文件系统路径。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/diagnose.mjs` around lines 283 - 285, Update the entry-point guard
surrounding main to use Node 24’s native import.meta.main check instead of
comparing import.meta.url with a manually constructed process.argv path,
preserving the existing main(process.argv.slice(2)) invocation.

…n they are

The classifier previously reported a classification and left the reading to
the reader. A failure is only legible when the tool also says what the symptom
historically meant and when that reading stops holding. Each entry encodes a
symptom class from the corpus that pointed somewhere other than the fault:

- 400 + api_error was the forced tool_choice class (thinking block echoed into
  a second request the upstream rejected); the #45 --json-schema removal
  retires it, so a 400 now is flagged as a NEW, uncharacterized cause
- 503 + cpu overload is the host CPU gate, not an upstream outage
- schema_error is the model misreading the contract, deterministic, not infra
- 402 is the balance class; 524 is the relay origin timeout
- the collapsed "after N attempts: claude exited 1" hides five causes

Also tightens the shape-change canary: events with no recognized shapes now
report that the artifact shape may have changed, so a future diagnostic format
degrades loudly to unclassified rather than silently misreading.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: ENGINE-43-RECONCILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant