@@ -87,10 +87,50 @@ module.exports = async ({ github, context, core }) => {
8787
8888 let labelsToAdd = entry . labels_to_add || [ ] ;
8989 let labelsToRemove = entry . labels_to_remove || [ ] ;
90+ let existingLabels = [ ] ;
91+
92+ // Fetch existing labels early
93+ try {
94+ const { data : issueData } = await github . rest . issues . get ( {
95+ owner : context . repo . owner ,
96+ repo : context . repo . repo ,
97+ issue_number : issueNumber ,
98+ } ) ;
99+ existingLabels = issueData . labels . map ( ( l ) =>
100+ typeof l === 'string' ? l : l . name ,
101+ ) ;
102+ } catch ( e ) {
103+ core . warning (
104+ `Failed to fetch existing labels for #${ issueNumber } : ${ e . message } ` ,
105+ ) ;
106+ }
107+
108+ // Programmatic Priority Downgrade Logic
109+ if ( labelsToAdd . includes ( 'status/need-information' ) ) {
110+ const targetPriority = labelsToAdd . find ( ( l ) => l . startsWith ( 'priority/' ) ) ;
111+ if ( targetPriority ) {
112+ let downgradedPriority = null ;
113+ if ( targetPriority === 'priority/p0' )
114+ downgradedPriority = 'priority/p1' ;
115+ if ( targetPriority === 'priority/p1' )
116+ downgradedPriority = 'priority/p2' ;
117+
118+ if ( downgradedPriority ) {
119+ core . info (
120+ `Programmatically downgrading ${ targetPriority } to ${ downgradedPriority } due to status/need-information` ,
121+ ) ;
122+ labelsToAdd = labelsToAdd . filter ( ( l ) => l !== targetPriority ) ;
123+ labelsToAdd . push ( downgradedPriority ) ;
124+ }
125+ }
126+ }
90127
91128 labelsToRemove . push ( 'status/need-triage' ) ;
92129
93- if ( labelsToAdd . includes ( 'status/manual-triage' ) ) {
130+ if (
131+ labelsToAdd . includes ( 'status/manual-triage' ) ||
132+ existingLabels . includes ( 'status/manual-triage' )
133+ ) {
94134 // If the AI flagged it for manual triage, remove bot-triaged if it exists
95135 labelsToRemove . push ( 'status/bot-triaged' ) ;
96136 // Ensure we don't accidentally try to add bot-triaged if the AI returned it
@@ -105,48 +145,24 @@ module.exports = async ({ github, context, core }) => {
105145 labelsToRemove = [ ...new Set ( labelsToRemove ) ] ;
106146
107147 // Fetch existing labels to auto-resolve conflicts
108- try {
109- const { data : issueData } = await github . rest . issues . get ( {
110- owner : context . repo . owner ,
111- repo : context . repo . repo ,
112- issue_number : issueNumber ,
113- } ) ;
114- const existingLabels = issueData . labels . map ( ( l ) =>
115- typeof l === 'string' ? l : l . name ,
116- ) ;
117-
118- const hasNewArea = labelsToAdd . some ( ( l ) => l . startsWith ( 'area/' ) ) ;
119- if ( hasNewArea ) {
120- const existingAreas = existingLabels . filter ( ( l ) =>
121- l . startsWith ( 'area/' ) ,
122- ) ;
123- labelsToRemove . push ( ...existingAreas ) ;
124- }
125-
126- const hasNewPriority = labelsToAdd . some ( ( l ) => l . startsWith ( 'priority/' ) ) ;
127- if ( hasNewPriority ) {
128- const existingPriorities = existingLabels . filter ( ( l ) =>
129- l . startsWith ( 'priority/' ) ,
130- ) ;
131- labelsToRemove . push ( ...existingPriorities ) ;
132- }
133-
134- const hasNewKind = labelsToAdd . some ( ( l ) => l . startsWith ( 'kind/' ) ) ;
135- if ( hasNewKind ) {
136- const existingKinds = existingLabels . filter ( ( l ) =>
137- l . startsWith ( 'kind/' ) ,
138- ) ;
139- labelsToRemove . push ( ...existingKinds ) ;
140- }
148+ const hasNewArea = labelsToAdd . some ( ( l ) => l . startsWith ( 'area/' ) ) ;
149+ if ( hasNewArea ) {
150+ const existingAreas = existingLabels . filter ( ( l ) => l . startsWith ( 'area/' ) ) ;
151+ labelsToRemove . push ( ...existingAreas ) ;
152+ }
141153
142- // Re-deduplicate and filter out labels we are trying to add
143- labelsToRemove = [ ...new Set ( labelsToRemove ) ] . filter (
144- ( l ) => ! labelsToAdd . includes ( l ) ,
145- ) ;
146- } catch ( e ) {
147- core . warning (
148- `Failed to fetch existing labels for #${ issueNumber } : ${ e . message } ` ,
154+ const hasNewPriority = labelsToAdd . some ( ( l ) => l . startsWith ( 'priority/' ) ) ;
155+ if ( hasNewPriority ) {
156+ const existingPriorities = existingLabels . filter ( ( l ) =>
157+ l . startsWith ( 'priority/' ) ,
149158 ) ;
159+ labelsToRemove . push ( ...existingPriorities ) ;
160+ }
161+
162+ const hasNewKind = labelsToAdd . some ( ( l ) => l . startsWith ( 'kind/' ) ) ;
163+ if ( hasNewKind ) {
164+ const existingKinds = existingLabels . filter ( ( l ) => l . startsWith ( 'kind/' ) ) ;
165+ labelsToRemove . push ( ...existingKinds ) ;
150166 }
151167
152168 // Enforce mutually exclusive area labels
@@ -175,6 +191,13 @@ module.exports = async ({ github, context, core }) => {
175191 ) ;
176192 }
177193
194+ // Re-deduplicate and filter out labels we are trying to add,
195+ // and filter out labels that are already present or absent to avoid unnecessary API calls
196+ labelsToRemove = [ ...new Set ( labelsToRemove ) ] . filter (
197+ ( l ) => ! labelsToAdd . includes ( l ) && existingLabels . includes ( l ) ,
198+ ) ;
199+ labelsToAdd = labelsToAdd . filter ( ( l ) => ! existingLabels . includes ( l ) ) ;
200+
178201 if ( labelsToAdd . length > 0 ) {
179202 await github . rest . issues . addLabels ( {
180203 owner : context . repo . owner ,
@@ -211,25 +234,36 @@ module.exports = async ({ github, context, core }) => {
211234 ) ;
212235 }
213236
214- if (
215- ( entry . explanation && process . env . SUPPRESS_COMMENT !== 'true' ) ||
216- entry . effort_analysis
217- ) {
237+ // Restrictive Commenting Policy:
238+ // - Silence standard triage (Area/Kind/Priority) to avoid spam.
239+ // - Only comment if status/need-information is added (to explain what is missing).
240+ // - Only comment if effort_analysis is present (deep technical dive).
241+ const needsInfoAdded =
242+ labelsToAdd . includes ( 'status/need-information' ) &&
243+ ! existingLabels . includes ( 'status/need-information' ) ;
244+ const hasEffortAnalysis = ! ! entry . effort_analysis ;
245+
246+ if ( needsInfoAdded || hasEffortAnalysis ) {
218247 let commentBody = '' ;
219- if ( entry . explanation && process . env . SUPPRESS_COMMENT !== 'true' ) {
248+ if ( needsInfoAdded && entry . explanation ) {
220249 commentBody += entry . explanation ;
221250 }
222- if ( entry . effort_analysis ) {
251+ if ( hasEffortAnalysis ) {
223252 if ( commentBody ) commentBody += '\n\n' ;
224253 commentBody += `**Effort Analysis:**\n${ entry . effort_analysis } ` ;
225254 }
226255
227- await github . rest . issues . createComment ( {
228- owner : context . repo . owner ,
229- repo : context . repo . repo ,
230- issue_number : issueNumber ,
231- body : commentBody ,
232- } ) ;
256+ if ( commentBody ) {
257+ await github . rest . issues . createComment ( {
258+ owner : context . repo . owner ,
259+ repo : context . repo . repo ,
260+ issue_number : issueNumber ,
261+ body : commentBody ,
262+ } ) ;
263+ core . info (
264+ `Posted required comment (need-info or effort) for #${ issueNumber } ` ,
265+ ) ;
266+ }
233267 }
234268
235269 if (
0 commit comments