Skip to content

fix(eslint): harden require-await-core-summary-write for compound expression forms#43507

Merged
pelikhan merged 4 commits into
mainfrom
copilot/require-await-core-summary-write
Jul 5, 2026
Merged

fix(eslint): harden require-await-core-summary-write for compound expression forms#43507
pelikhan merged 4 commits into
mainfrom
copilot/require-await-core-summary-write

Conversation

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

The require-await-core-summary-write rule only inspected direct ExpressionStatement → CallExpression nodes, silently missing Promise<Summary> drops in compound expression forms:

async function f() {
  cond && core.summary.write();           // LogicalExpression  — FN
  cond ? core.summary.write() : noop();   // ConditionalExpression — FN
  (log(), core.summary.write());          // SequenceExpression — FN
  core.summary.write().then(() => {});    // .then() chain — FN
}

Changes

  • collectBareWriteCalls(expr) — new recursive helper inside create() that replaces the inline guard. Unwraps:

    • LogicalExpression → right operand only
    • ConditionalExpression → both branches
    • SequenceExpression → last element (with defensive empty-array guard)
    • UnaryExpression[void] → immediately returns [] (deliberate-discard, exempt)
    • .then()/.catch()/.finally() chains on a write() call → flags the outer expression; restricted to known Promise method names to avoid false positives on arbitrary chained calls
  • ExpressionStatement visitor — refactored to call collectBareWriteCalls and loop over results; the await-insertion suggestion continues to work correctly for each flagged node within its compound context.

  • Tests — new it blocks for void exemption (valid) and all four compound forms (invalid), including a double-error case where both branches of a conditional call write().


Generated by 👨‍🍳 PR Sous Chef · 8.12 AIC · ⌖ 9.29 AIC · ⊞ 3.7K ·
Comment /souschef to run again

…ression forms

- Add collectBareWriteCalls() helper to unwrap LogicalExpression (right
  operand), ConditionalExpression (both branches), and SequenceExpression
  (last element) before the CallExpression check
- Detect .then()/.catch()/.finally() chains on write() calls and flag the
  outer expression (restricted to known Promise methods to avoid FPs)
- Exempt void core.summary.write() as the idiomatic deliberate-discard marker
- Add invalid tests for Logical / Conditional / Sequence / .then() forms
- Add valid test for void exemption

Closes #43326

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix require-await rule to detect discarded promises in expressions fix(eslint): harden require-await-core-summary-write for compound expression forms Jul 5, 2026
Copilot AI requested a review from pelikhan July 5, 2026 08:19
@pelikhan
pelikhan marked this pull request as ready for review July 5, 2026 08:25
Copilot AI review requested due to automatic review settings July 5, 2026 08:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the require-await-core-summary-write ESLint rule so it detects dropped Promise<Summary> values from core.summary.write() even when the call is embedded in compound expressions (logical/conditional/sequence expressions and Promise chain methods), and adds tests for these previously missed forms.

Changes:

  • Add collectBareWriteCalls(expr) helper to recursively find bare write() calls inside several compound expression shapes.
  • Refactor the ExpressionStatement visitor to report (and suggest await for) each matched call expression.
  • Add new tests covering compound-expression false negatives and void deliberate-discard exemption.
Show a summary per file
File Description
eslint-factory/src/rules/require-await-core-summary-write.ts Adds recursive collector and refactors reporting/suggestions to catch compound-expression dropped write() promises.
eslint-factory/src/rules/require-await-core-summary-write.test.ts Adds new valid/invalid cases for compound expressions and void exemption.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 4
  • Review effort level: Low

Comment on lines +199 to +202
// Logical short-circuit: only the right operand can be the dropped promise
if (expr.type === "LogicalExpression") {
return collectBareWriteCalls(expr.right);
}
Comment on lines +209 to +217
// Sequence: only the last expression is the "result" value.
// SequenceExpression always has ≥2 elements per the ECMAScript spec, but
// guard defensively since the type signature allows an empty array.
if (expr.type === "SequenceExpression") {
const { expressions } = expr;
if (expressions.length === 0) return [];
const last = expressions[expressions.length - 1];
return collectBareWriteCalls(last);
}
Comment thread eslint-factory/src/rules/require-await-core-summary-write.test.ts
Comment thread eslint-factory/src/rules/require-await-core-summary-write.test.ts
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (0 additions detected).

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 2 test(s): 2 design, 0 implementation, 0 violation(s).

📊 Metrics (2 tests)
Metric Value
Analyzed 2 (Go: 0, JS/TS: 2)
✅ Design 2 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 2 (100%)
Duplicate clusters 0
Inflation No (0.49:1 — well under 2:1 threshold)
🚨 Violations 0
Test File Classification Issues
valid: void core.summary.write() is not flagged require-await-core-summary-write.test.ts:193 ✅ design_test · high_value None
invalid: write() in compound expression forms is flagged require-await-core-summary-write.test.ts:200 ✅ design_test · high_value None

Verdict

Passed. 0% implementation tests (threshold: 30%). Both new tests enforce genuine behavioral contracts with multiple edge-case scenarios.

valid: void core.summary.write() is not flagged — Design test codifying that void expr is the idiomatic deliberate-discard marker and must not be flagged. Covers both direct and chained (void core.summary.addRaw(x).write()) forms. Prevents false positives for intentional usage.

invalid: write() in compound expression forms is flagged — Design test covering five distinct compound expression patterns that silently drop the returned Promise<Summary>: LogicalExpression (cond && write()), ConditionalExpression (one branch), ConditionalExpression (both branches — two errors emitted), SequenceExpression ((log(), write())), and .then() chain. The two-errors case for the both-branches conditional is a non-trivial edge that proves the rule correctly walks all promise-dropping positions in a single statement.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 51.8 AIC · ⌖ 11.8 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%). Both new tests are high-value design tests with thorough edge-case coverage.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — overall a clean, well-structured fix with good coverage. Left a few suggestions around edge case documentation and missing test variants.

📋 Key Themes & Highlights

Key Themes

  • Missing ||/?? test coverage: The LogicalExpression handler covers all three operators but only && is tested. Adding || and ?? cases would lock in the behaviour.
  • Multi-level .then().catch() chain: A doubly-chained form like write().then(f).catch(e) is not flagged by the current logic. Whether that's intentional (to avoid FPs) or an oversight should be documented or tested.
  • void compound form: void (cond && write()) is already exempt by the implementation, but no test locks in this behaviour.
  • ESLint suggestion independence: The double-error ConditionalExpression case is tested correctly; a brief inline comment would make the single-at-a-time assumption explicit for future maintainers.

Positive Highlights

  • ✅ Excellent recursive design for collectBareWriteCalls — clean separation from the visitor
  • void exemption is well-motivated and properly handled as a first-class escape hatch
  • ✅ The double-error conditional test is a great regression anchor for multiple-suggestion scenarios
  • ✅ Defensive empty-array guard on SequenceExpression is a nice touch given the type signature
  • ✅ Promise chain restriction to then/catch/finally avoids false positives on arbitrary chained methods

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 68 AIC · ⌖ 5.81 AIC · ⊞ 6.6K
Comment /matt to run again


// Logical short-circuit: only the right operand can be the dropped promise
if (expr.type === "LogicalExpression") {
return collectBareWriteCalls(expr.right);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The comment says "right operand only (the potential dropped value)" but doesn't explain why ||/?? left operands are safe to skip. For cond || core.summary.write() the right operand is only evaluated when cond is falsy, which is still a dropped Promise worth flagging — yet for cond ?? write() the left would never be a write call, so right-only is always correct. A brief comment clarifying the reasoning would prevent future readers from second-guessing this choice.

💡 Suggested clarification
// For all logical operators (&&, ||, ??), the right operand is the value that
// can be produced and immediately discarded as a bare ExpressionStatement.
// The left operand is always evaluated for its truthiness — it cannot itself
// be silently dropped — so right-only recursion is the correct conservative policy.

@copilot please address this.

Comment thread eslint-factory/src/rules/require-await-core-summary-write.ts
Comment thread eslint-factory/src/rules/require-await-core-summary-write.test.ts
Comment thread eslint-factory/src/rules/require-await-core-summary-write.test.ts
Comment thread eslint-factory/src/rules/require-await-core-summary-write.ts

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fix(eslint): harden require-await-core-summary-write for compound expression forms

The implementation is correct and well-reasoned. The new collectBareWriteCalls helper cleanly handles all four compound expression forms (logical, conditional, sequence, promise chains) and the tests thoroughly cover both the valid (void-exemption) and invalid cases including the multi-error conditional.

Highlights:

  • void exemption is correctly handled as the outermost guard — deliberate-discard pattern won't produce false positives
  • Reporting against callExpr (not node) gives more precise location and makes the await-insertion suggestion target the right node
  • Promise chain detection is conservatively restricted to then/catch/finally — no false-positive risk on arbitrary method chains
  • The double-error test for cond ? write() : write() validates independent suggestion application correctly

Non-blocking suggestions (inline comments):

  1. Line 201 — inline comment says "only the right operand can be the dropped promise" which is slightly misleading (left-side write() in write() && cond is an acknowledged gap, not because the right is the only possible drop). Suggest wording the comment to make the gap explicit.
  2. Line 195collectBareWriteCalls has no depth guard, unlike rootsSummaryOrAlias. A simple depth > 32 guard would make it consistent and prevent hypothetical deep-recursion in malformed ASTs.
  3. Line 212 — Non-last sequence elements with write() calls are silently ignored. The behavior is defensible, but a comment acknowledging this as intentional would improve readability.

All suggestions are non-blocking. The change is a net improvement in linter coverage. ✅

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 94.7 AIC · ⌖ 6.23 AIC · ⊞ 4.9K


// Logical short-circuit: only the right operand can be the dropped promise
if (expr.type === "LogicalExpression") {
return collectBareWriteCalls(expr.right);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "only the right operand can be the dropped promise", but the left operand of && is also evaluated and its Promise is discarded when it is a bare write call:

core.summary.write() && doSomething()  // left-side write() Promise silently dropped — not flagged

This is a known gap (the PR description focuses on right-operand / result-value drops), but the inline comment is misleading. Consider rewording to:

// Logical: only the right operand is unwrapped (it carries the statement's result value).
// Left-side write() drops (e.g. write() && cond) are a known out-of-scope gap.

Non-blocking suggestion. @copilot please address this.

Comment thread eslint-factory/src/rules/require-await-core-summary-write.ts
Comment thread eslint-factory/src/rules/require-await-core-summary-write.ts
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the remaining feedback, ensure the branch is current, and rerun checks.

Generated by 👨‍🍳 PR Sous Chef · 16.1 AIC · ⌖ 6.23 AIC · ⊞ 4.6K ·
Comment /souschef to run again

…xpression elements

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, review the remaining feedback, ensure the branch is current, and rerun checks.

...

Done. Both issues from the review are addressed in commit eeb9b7d:

  • LogicalExpression: now inspects both left and right operands — write() && cond is correctly flagged.
  • SequenceExpression: now inspects all elements via flatMap(core.summary.write(), log()) is correctly flagged.
  • Tests added for both new cases.

All 129 eslint-factory tests pass. The only test-unit failure (TestPRSousChefWorkflowAddCommentTargetContract) is pre-existing and unrelated to these changes.

Copilot AI requested a review from gh-aw-bot July 5, 2026 09:43
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address any unresolved review feedback, ensure the branch is up to date with the base branch, and rerun checks.

Generated by 👨‍🍳 PR Sous Chef · 8.37 AIC · ⌖ 7.92 AIC · ⊞ 3.7K ·
Comment /souschef to run again

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address any unresolved review feedback, ensure the branch is up to date with the base branch, and rerun checks.

...

All review feedback was addressed in commit eeb9b7d. Local validation is green: make fmt, make lint, and all 129 eslint-factory tests pass. The Smoke CI check shows action_required with zero jobs — it needs a human to approve the workflow run before it executes. No code changes are needed.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage — Run §28741077056

Field Value
Category bug
Risk 🟢 Low
Priority Score 55/100 — impact:25 urgency:15 quality:15
Action 🚀 fast_track

Rationale: ESLint rule fix capturing previously silently-missed Promise drops in compound expressions. 2× APPROVED. JS-only change with tests included.

Generated by 🔧 PR Triage Agent · 91.2 AIC · ⌖ 11.9 AIC · ⊞ 5.5K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 8.12 AIC · ⌖ 9.29 AIC · ⊞ 3.7K ·
Comment /souschef to run again

@pelikhan
pelikhan merged commit a6d310f into main Jul 5, 2026
6 checks passed
@pelikhan
pelikhan deleted the copilot/require-await-core-summary-write branch July 5, 2026 13:06
Copilot stopped work on behalf of gh-aw-bot due to an error July 5, 2026 13:08
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

require-await-core-summary-write: misses discarded promise in non-bare-call statement positions

4 participants