Repo: JSONbored/gittensory
File: src/signals/reward-risk.ts (lines 274 and 372)
Severity: medium (correctness — contributor-facing guidance silently loses distinct actions)
Summary
Two nextActions aggregations put .slice(0, N) inside new Set(...), so
they truncate the flattened list to N entries and then deduplicate — yielding
fewer than N unique actions whenever the first N contain duplicates, while
genuinely-distinct actions further down the list are dropped even though there is
room for them. Every other dedup-and-cap in the codebase does the opposite order
(dedupe, then cap).
Evidence
// src/signals/reward-risk.ts:274 (per-repo)
const nextActions = [...new Set(actions.flatMap((action) => action.nextActions).slice(0, 8))];
// src/signals/reward-risk.ts:372 (cross-repo aggregate)
const nextActions = [...new Set(topActions.flatMap((action) => action.nextActions).slice(0, 10))];
The .slice() is applied to the flattened array before it is passed to
new Set(...), so the cap is consumed by duplicates.
Inconsistent with the codebase's own convention
Every sibling dedups first, then caps — including the identical
flatMap-nextActions-dedup-cap in decision-pack:
src/services/decision-pack.ts:619 — [...new Set([...monitorNextSteps, ...topActions.flatMap((a) => a.nextActions)])].slice(0, 12) ✅
src/queue/processors.ts:342,354 — [...new Set(...flatMap(...))].slice(0, 200/500) ✅
src/services/agent-orchestrator.ts:616, src/services/queue-trends.ts:126,
src/signals/local-branch.ts:1067,1113, src/services/agent-action-explanation-card.ts:102 — all [...new Set(...)].slice(0, N) ✅
reward-risk.ts:274 and :372 are the only two sites that slice inside the Set.
Concrete trace (line 372)
topActions is the cross-repo aggregate
(repoAnalyses.flatMap((a) => a.actions) … .slice(0, 12)), and each action's
nextActions = nextActionsFor(kind) returns the same string for a given kind
regardless of repo. So when the top repos share an action kind (e.g. the five
highest-priority repos all recommend open_new_direct_pr), the flattened list
starts with that identical string repeated:
["Only open a new PR after …", // repo A (open_new_direct_pr)
"Only open a new PR after …", // repo B (open_new_direct_pr)
"Only open a new PR after …", // repo C
"Land, close, or withdraw …", "Prioritize the repo where …", // repo D (cleanup_existing_prs)
"Only open a new PR after …", // repo E
…
"File only high-proof issues …", // repo H (file_issue_discovery) ← position 11+
"Improve labels, contribution docs …" // repo I (maintainer_lane) ← position 12+
]
- Current:
slice(0, 10) keeps the first 10 (mostly the repeated
open_new_direct_pr string) → new Set collapses them to ~3 unique →
file_issue_discovery / maintainer_lane steps at positions 11+ are dropped.
- Intended:
[...new Set(flat)].slice(0, 10) collapses the repeats first,
then keeps up to 10 distinct steps — surfacing the issue-discovery and
maintainer-lane actions.
So the contributor's top-level strategy nextActions shows a handful of
duplicated-then-deduped steps and silently omits distinct, actionable guidance
that would fit within the cap.
Test status
No test locks in the current behavior — there is no reward-risk /
reviewability unit test file, and no test asserts nextActions length or
contents.
Suggested fix
Move the cap outside the Set, matching the rest of the codebase:
// line 274
const nextActions = [...new Set(actions.flatMap((action) => action.nextActions))].slice(0, 8);
// line 372
const nextActions = [...new Set(topActions.flatMap((action) => action.nextActions))].slice(0, 10);
Add a fixture where several top repos share an action kind and assert the
aggregate nextActions still surfaces the distinct lower-priority steps.
Repo: JSONbored/gittensory
File:
src/signals/reward-risk.ts(lines 274 and 372)Severity: medium (correctness — contributor-facing guidance silently loses distinct actions)
Summary
Two
nextActionsaggregations put.slice(0, N)insidenew Set(...), sothey truncate the flattened list to N entries and then deduplicate — yielding
fewer than N unique actions whenever the first N contain duplicates, while
genuinely-distinct actions further down the list are dropped even though there is
room for them. Every other dedup-and-cap in the codebase does the opposite order
(dedupe, then cap).
Evidence
The
.slice()is applied to the flattened array before it is passed tonew Set(...), so the cap is consumed by duplicates.Inconsistent with the codebase's own convention
Every sibling dedups first, then caps — including the identical
flatMap-nextActions-dedup-cap in decision-pack:
src/services/decision-pack.ts:619—[...new Set([...monitorNextSteps, ...topActions.flatMap((a) => a.nextActions)])].slice(0, 12)✅src/queue/processors.ts:342,354—[...new Set(...flatMap(...))].slice(0, 200/500)✅src/services/agent-orchestrator.ts:616,src/services/queue-trends.ts:126,src/signals/local-branch.ts:1067,1113,src/services/agent-action-explanation-card.ts:102— all[...new Set(...)].slice(0, N)✅reward-risk.ts:274and:372are the only two sites that slice inside the Set.Concrete trace (line 372)
topActionsis the cross-repo aggregate(
repoAnalyses.flatMap((a) => a.actions) … .slice(0, 12)), and each action'snextActions = nextActionsFor(kind)returns the same string for a given kindregardless of repo. So when the top repos share an action kind (e.g. the five
highest-priority repos all recommend
open_new_direct_pr), the flattened liststarts with that identical string repeated:
slice(0, 10)keeps the first 10 (mostly the repeatedopen_new_direct_prstring) →new Setcollapses them to ~3 unique →file_issue_discovery/maintainer_lanesteps at positions 11+ are dropped.[...new Set(flat)].slice(0, 10)collapses the repeats first,then keeps up to 10 distinct steps — surfacing the issue-discovery and
maintainer-lane actions.
So the contributor's top-level strategy
nextActionsshows a handful ofduplicated-then-deduped steps and silently omits distinct, actionable guidance
that would fit within the cap.
Test status
No test locks in the current behavior — there is no
reward-risk/reviewabilityunit test file, and no test assertsnextActionslength orcontents.
Suggested fix
Move the cap outside the
Set, matching the rest of the codebase:Add a fixture where several top repos share an action kind and assert the
aggregate
nextActionsstill surfaces the distinct lower-priority steps.