fix(learnings): cross-project trust gate is an allowlist, not a denylist (#1745)

gstack-learnings-search --cross-project is documented as an allowlist — foreign
learnings load only when user-stated/trusted, to stop one project's AI-generated
learnings from injecting into another project's reviews. It was implemented as a
denylist: `if (isCrossProject && e.trusted === false) continue`. Any row where
`trusted` is missing/undefined (legacy rows from before the field existed,
hand-edited rows, rows from other tools) passed `undefined === false` → false →
admitted. Those rows leaked across projects.

Flip to `e.trusted !== true`. Test: a foreign row with no `trusted` field is now
excluded (true still included, false still excluded).

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-07 22:49:36 -07:00
parent be3d4c7171
commit 549f32a8f9
2 changed files with 15 additions and 2 deletions
+5 -2
View File
@@ -90,10 +90,13 @@ for (const taggedLine of lines) {
const isCrossProject = sourceTag === 'cross';
e._crossProject = isCrossProject;
// Trust gate: cross-project learnings only loaded if trusted (user-stated)
// Trust gate: cross-project learnings only loaded if trusted (user-stated).
// This prevents prompt injection from one project's AI-generated learnings
// silently influencing reviews in another project.
if (isCrossProject && e.trusted === false) continue;
// #1745: this is an ALLOWLIST, not a denylist. `=== false` admitted any row
// where `trusted` is missing/undefined (legacy rows written before the field
// existed, hand-edited rows, rows from other tools). Require trusted === true.
if (isCrossProject && e.trusted !== true) continue;
entries.push(e);
} catch {}