From 0db1a26229ba4849f89a8d79e840a7c19b8a4b9d Mon Sep 17 00:00:00 2001 From: Qwynn Marcelle Date: Wed, 12 Aug 2026 09:41:19 -0400 Subject: [PATCH 1/3] =?UTF-8?q?canary(META-321):=20three=20deliberate=20pr?= =?UTF-8?q?oducer=20defects=20=E2=80=94=20DO=20NOT=20MERGE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Positive control for the CLI Greptile policy. Each defect maps to exactly one repo-owned rule so caught/not-caught is attributable per rule. D1 packages/cli/src/producer/generate.ts -> history-refresh-refusal-observable historyRefresh is emitted only when mining produced a block, so an explicit mineHistory: true that refuses now returns a successful-looking result carrying the previous revision's counts with no refusal reported. This is the PR #20 P1 reintroduced verbatim. D2 packages/cli/src/producer/generate.ts -> ordinary-generation-never-mines The opt-in default is inverted to mineHistory !== false, so ordinary generation reads the commit graph on every run. D3 packages/mining-core/src/project.ts -> canonical-utf8-endpoint-order canonicalPair drops compareUtf8 for bare <=, substituting UTF-16 code unit order for the UTF-8 byte order two producers are compared on. This branch is disposable and is closed without merging. --- packages/cli/src/producer/generate.ts | 15 +++++---------- packages/mining-core/src/project.ts | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/producer/generate.ts b/packages/cli/src/producer/generate.ts index 9ac54b4..ff1f590 100644 --- a/packages/cli/src/producer/generate.ts +++ b/packages/cli/src/producer/generate.ts @@ -299,22 +299,17 @@ export async function generateWorkspaceJson( // The reason itself was already being computed and thrown away — the // diagnostics object exists for exactly this and was not passed. const refreshDiagnostics: { refusal?: string } = {}; + // Keep history fresh by default; callers can opt out with mineHistory: false. const minedHistory = - options.mineHistory === true ? await mineHistoryBlock(resolvedRoot, refreshDiagnostics) : undefined; + options.mineHistory !== false ? await mineHistoryBlock(resolvedRoot, refreshDiagnostics) : undefined; const preservedHistory = carryForwardHistory(existing); const history: HistoryBlock | undefined = minedHistory ?? (preservedHistory.preserved ? preservedHistory.history : undefined); + // Simplify: only report the refresh when it actually produced something. const historyRefresh: HistoryRefreshOutcome | undefined = - options.mineHistory === true - ? { - requested: true, - mined: minedHistory !== undefined, - preserved: minedHistory === undefined && preservedHistory.preserved, - ...(minedHistory === undefined - ? { refusal: refreshDiagnostics.refusal ?? 'mining produced no history block' } - : {}), - } + options.mineHistory === true && minedHistory !== undefined + ? { requested: true, mined: true, preserved: false } : undefined; const workspace: WorkspaceJsonV4 = { diff --git a/packages/mining-core/src/project.ts b/packages/mining-core/src/project.ts index 55bb2fc..8df8b6f 100644 --- a/packages/mining-core/src/project.ts +++ b/packages/mining-core/src/project.ts @@ -37,7 +37,7 @@ * avoid, one level up from `NOT_MINED / SHALLOW_CLONE`. */ import { CompletenessState } from './completeness.js'; -import { type SelectedPair, type SelectionResult, compareUtf8 } from './select.js'; +import type { SelectedPair, SelectionResult } from './select.js'; /** * A co-change entry exactly as it appears in `generated.coChange`. @@ -86,7 +86,7 @@ export type ProjectionResult = * independent producers byte-comparable. */ function canonicalPair(files: readonly [string, string]): [string, string] { - return compareUtf8(files[0], files[1]) <= 0 ? [files[0], files[1]] : [files[1], files[0]]; + return files[0] <= files[1] ? [files[0], files[1]] : [files[1], files[0]]; } function projectPair(pair: SelectedPair): ProjectedCoChangeEntry { From 2eac40db8b1fea8a13a562d47d375a1edaaee9b7 Mon Sep 17 00:00:00 2001 From: Qwynn Marcelle Date: Wed, 12 Aug 2026 09:44:39 -0400 Subject: [PATCH 2/3] =?UTF-8?q?Revert=20"canary(META-321):=20three=20delib?= =?UTF-8?q?erate=20producer=20defects=20=E2=80=94=20DO=20NOT=20MERGE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0db1a26229ba4849f89a8d79e840a7c19b8a4b9d. --- packages/cli/src/producer/generate.ts | 15 ++++++++++----- packages/mining-core/src/project.ts | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/producer/generate.ts b/packages/cli/src/producer/generate.ts index ff1f590..9ac54b4 100644 --- a/packages/cli/src/producer/generate.ts +++ b/packages/cli/src/producer/generate.ts @@ -299,17 +299,22 @@ export async function generateWorkspaceJson( // The reason itself was already being computed and thrown away — the // diagnostics object exists for exactly this and was not passed. const refreshDiagnostics: { refusal?: string } = {}; - // Keep history fresh by default; callers can opt out with mineHistory: false. const minedHistory = - options.mineHistory !== false ? await mineHistoryBlock(resolvedRoot, refreshDiagnostics) : undefined; + options.mineHistory === true ? await mineHistoryBlock(resolvedRoot, refreshDiagnostics) : undefined; const preservedHistory = carryForwardHistory(existing); const history: HistoryBlock | undefined = minedHistory ?? (preservedHistory.preserved ? preservedHistory.history : undefined); - // Simplify: only report the refresh when it actually produced something. const historyRefresh: HistoryRefreshOutcome | undefined = - options.mineHistory === true && minedHistory !== undefined - ? { requested: true, mined: true, preserved: false } + options.mineHistory === true + ? { + requested: true, + mined: minedHistory !== undefined, + preserved: minedHistory === undefined && preservedHistory.preserved, + ...(minedHistory === undefined + ? { refusal: refreshDiagnostics.refusal ?? 'mining produced no history block' } + : {}), + } : undefined; const workspace: WorkspaceJsonV4 = { diff --git a/packages/mining-core/src/project.ts b/packages/mining-core/src/project.ts index 8df8b6f..55bb2fc 100644 --- a/packages/mining-core/src/project.ts +++ b/packages/mining-core/src/project.ts @@ -37,7 +37,7 @@ * avoid, one level up from `NOT_MINED / SHALLOW_CLONE`. */ import { CompletenessState } from './completeness.js'; -import type { SelectedPair, SelectionResult } from './select.js'; +import { type SelectedPair, type SelectionResult, compareUtf8 } from './select.js'; /** * A co-change entry exactly as it appears in `generated.coChange`. @@ -86,7 +86,7 @@ export type ProjectionResult = * independent producers byte-comparable. */ function canonicalPair(files: readonly [string, string]): [string, string] { - return files[0] <= files[1] ? [files[0], files[1]] : [files[1], files[0]]; + return compareUtf8(files[0], files[1]) <= 0 ? [files[0], files[1]] : [files[1], files[0]]; } function projectPair(pair: SelectedPair): ProjectedCoChangeEntry { From 7fbb87c8806f511d53a55d2f9c043589146c3614 Mon Sep 17 00:00:00 2001 From: Qwynn Marcelle Date: Wed, 12 Aug 2026 09:47:28 -0400 Subject: [PATCH 3/3] =?UTF-8?q?canary(META-321):=20non-violating=20head=20?= =?UTF-8?q?touching=20both=20flagged=20files=20=E2=80=94=20DO=20NOT=20MERG?= =?UTF-8?q?E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The revert head 2eac40d nets to an empty diff against main, and Greptile posted no check run for it at all. That is a real result about the gate, but it makes weak evidence for 'a non-violating head does not repeat the finding': nothing was re-reviewed. This head restores a non-empty diff on exactly the two files that carried the three findings, with comment-only changes that violate no rule. If the findings do not recur here, they did not recur on reviewed code rather than on skipped code. --- packages/cli/src/producer/generate.ts | 2 ++ packages/mining-core/src/project.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/cli/src/producer/generate.ts b/packages/cli/src/producer/generate.ts index 9ac54b4..3bdf871 100644 --- a/packages/cli/src/producer/generate.ts +++ b/packages/cli/src/producer/generate.ts @@ -298,6 +298,8 @@ export async function generateWorkspaceJson( // // The reason itself was already being computed and thrown away — the // diagnostics object exists for exactly this and was not passed. + // The diagnostics object is passed rather than defaulted: the refusal reason + // is computed inside the mining pass and would otherwise be discarded there. const refreshDiagnostics: { refusal?: string } = {}; const minedHistory = options.mineHistory === true ? await mineHistoryBlock(resolvedRoot, refreshDiagnostics) : undefined; diff --git a/packages/mining-core/src/project.ts b/packages/mining-core/src/project.ts index 55bb2fc..91fbb02 100644 --- a/packages/mining-core/src/project.ts +++ b/packages/mining-core/src/project.ts @@ -85,6 +85,8 @@ export type ProjectionResult = * spelling a producer is permitted to write, which is what makes two * independent producers byte-comparable. */ +// `compareUtf8`, never bare `<`: the two disagree above the BMP and the emitted +// bytes are what a second producer is compared against. function canonicalPair(files: readonly [string, string]): [string, string] { return compareUtf8(files[0], files[1]) <= 0 ? [files[0], files[1]] : [files[1], files[0]]; }