From 80abf6334039a0f38dd2eaf09a562aa7b21e0871 Mon Sep 17 00:00:00 2001 From: Carlos Mora Date: Sun, 18 Oct 2026 07:19:53 -0500 Subject: [PATCH 1/2] fix(review): preserve candidate-owner preparation errno in diagnostic message (#2376) --- extensions/gentle-ai.ts | 2 +- ...date-owner-preparation-errno-diagnostic.md | 28 +++++++++++++++++++ tests/gentle-ai.test.ts | 4 +-- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 odd/tasks/fix-2376-candidate-owner-preparation-errno-diagnostic.md diff --git a/extensions/gentle-ai.ts b/extensions/gentle-ai.ts index 3b7ef77e6..1331f0290 100644 --- a/extensions/gentle-ai.ts +++ b/extensions/gentle-ai.ts @@ -7206,7 +7206,7 @@ function nativeOperationFailure(operation: ReviewControllerOperation | "gentle_r const preservesNativeTargetStatusDiagnostic = nativeDiagnostics?.operation === NATIVE_REVIEW_OPERATION.STATUS; const preservesAnswerConsentStartDiagnostic = operation === REVIEW_CONTROLLER_OPERATION.ANSWER_CONSENT && nativeDiagnostics?.operation === NATIVE_REVIEW_OPERATION.START; const diagnostics = operation === REVIEW_CONTROLLER_OPERATION.START && error instanceof CandidateViewError && value.candidateViewPreNative === true - ? error.diagnostics ?? { code: error.reason, message: "candidate view rejected before native START" } + ? error.diagnostics ?? { code: error.reason, message: error.message } : error instanceof CandidateViewError ? { code: error.reason, message: error.message } : nativeDiagnostics?.operation === `review/${operation}` || preservesNativeTargetStatusDiagnostic || preservesAnswerConsentStartDiagnostic diff --git a/odd/tasks/fix-2376-candidate-owner-preparation-errno-diagnostic.md b/odd/tasks/fix-2376-candidate-owner-preparation-errno-diagnostic.md new file mode 100644 index 000000000..b2d3774dc --- /dev/null +++ b/odd/tasks/fix-2376-candidate-owner-preparation-errno-diagnostic.md @@ -0,0 +1,28 @@ +# Task Breakdown: Fix #2376 Candidate Owner Preparation Errno Diagnostic + +**Feature:** `fix-2376-candidate-owner-preparation-errno-diagnostic` +**Issue:** #2376 (`gentle_review START: candidate-owner-preparation-failed drops the errno, leaving the failure undiagnosable`) +**Strategy:** Strict TDD, ODD workflow, Conventional Commits + +--- + +## Tasks + +### 1. Test (RED): Unit test for candidate owner preparation errno diagnostic +- [x] In `tests/gentle-ai.test.ts`: + - Update `ordinary START reports candidate-owner preparation failure as pre-native no mutation` to throw `new CandidateViewError("candidate view owner preparation failed (EACCES)", "candidate-owner-preparation-failed")`. + - Assert that `result.diagnostics` preserves `message: "candidate view owner preparation failed (EACCES)"`. + - Evidence: Red test failure `message: 'candidate view rejected before native START'` observed before fix. + +### 2. Implementation (GREEN): Preserve CandidateViewError message +- [x] In `extensions/gentle-ai.ts`: + - In `nativeOperationFailure`, update the pre-native `CandidateViewError` fallback from `"candidate view rejected before native START"` to `error.message`. + - Evidence: 123/123 tests passing in `tests/gentle-ai.test.ts`. + +### 3. Verification & Regressions +- [x] Run test suite: `node --experimental-strip-types --test tests/gentle-ai.test.ts`. +- [x] Run typecheck: `npm run typecheck` (0 diagnostics, 0 regressions). +- [x] Evidence: 0 regressions, all tests passing. + +### 4. Work-Unit Commit +- [x] Create conventional commit: `fix(review): preserve candidate-owner preparation errno in diagnostic message (#2376)`. diff --git a/tests/gentle-ai.test.ts b/tests/gentle-ai.test.ts index e6ed2942a..a1f771c36 100644 --- a/tests/gentle-ai.test.ts +++ b/tests/gentle-ai.test.ts @@ -1088,7 +1088,7 @@ test("ordinary START reports candidate-owner preparation failure as pre-native n start: async () => { nativeStarts += 1; throw new Error("native START must not run"); }, } as unknown as NativeReviewCli; const candidateViews = { - createOrReuse: () => { throw new CandidateViewError("candidate view owner preparation failed", "candidate-owner-preparation-failed"); }, + createOrReuse: () => { throw new CandidateViewError("candidate view owner preparation failed (EACCES)", "candidate-owner-preparation-failed"); }, } as unknown as CandidateViewRegistry; const result = await __testing.executeReviewControllerOperation( { operation: "start", input: JSON.stringify({ mode: "ordinary" }) }, @@ -1102,7 +1102,7 @@ test("ordinary START reports candidate-owner preparation failure as pre-native n assert.equal(result.mutation_outcome, "none"); assert.deepEqual(result.diagnostics, { code: "candidate-owner-preparation-failed", - message: "candidate view rejected before native START", + message: "candidate view owner preparation failed (EACCES)", }); let materializationStatusCalls = 0; From bc9ec9f6cd8cd171ec0c82734f22aebe1003e4f2 Mon Sep 17 00:00:00 2001 From: Carlos Mora Date: Mon, 26 Oct 2026 06:44:47 -0500 Subject: [PATCH 2/2] test(review): pin EACCES suffix and the bounded errno set in owner preparation message (#2376) --- tests/review-candidate-view.test.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/review-candidate-view.test.ts b/tests/review-candidate-view.test.ts index ead076403..fb8b68a46 100644 --- a/tests/review-candidate-view.test.ts +++ b/tests/review-candidate-view.test.ts @@ -732,6 +732,31 @@ test("owner preparation failure names the transient cause code in the wrapper me ); }); +test("owner preparation failure surfaces a denial code and omits a code outside the bounded set", (t) => { + const cwd = repository(t); + let code = "EACCES"; + t.mock.method(fs, "fsyncSync", () => { + const error = new Error("fixture denial") as NodeJS.ErrnoException; + error.code = code; + throw error; + }); + syncBuiltinESMExports(); + t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); }); + const messageFor = (): string => { + try { + new CandidateViewRegistry().create({ contributorRoot: cwd }); + } catch (error) { + assert.ok(error instanceof CandidateViewError); + assert.equal(error.reason, "candidate-owner-preparation-failed"); + return error.message; + } + throw new Error("owner preparation was expected to fail"); + }; + assert.equal(messageFor(), "candidate view owner preparation failed (EACCES)"); + code = "ENOSPC"; + assert.equal(messageFor(), "candidate view owner preparation failed"); +}); + test("ordinary cleanup preserves mismatched owner and cleanupAll continues other records", (t) => { const cwd = repository(t); const registry = new CandidateViewRegistry();